From c3c309a672c3c41446468a8c5f15219cfc1bb9be Mon Sep 17 00:00:00 2001 From: levy9527 Date: Thu, 23 Nov 2023 01:06:30 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20@=20levy9527?= =?UTF-8?q?/blog@dd684d3f43191d8571b25e2390f96a6eb29df405=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 404.html | 6 +- about.html | 8 +- article/index.html | 68 ++- ....html-fc95297b.js => 404.html-0b613605.js} | 2 +- ...roblems-in-IntelliJ-IDEA.html-73ad8a4f.js} | 2 +- ...roblems-in-IntelliJ-IDEA.html-ce1eab94.js} | 2 +- ...mon-Problems-in-Maven.md.html-9fd731f4.js} | 2 +- ...mon-Problems-in-Maven.md.html-f77903d5.js} | 2 +- ...ress2-entertaining-video.html-c410f2a7.js} | 2 +- ...ress2-entertaining-video.html-d7d94634.js} | 2 +- ...> a-warning-from-navicat.html-d1243c4d.js} | 2 +- ...> a-warning-from-navicat.html-dbede3aa.js} | 2 +- ...-things-you-need-to-know.html-a719ca33.js} | 2 +- ...-things-you-need-to-know.html-b26fd653.js} | 2 +- ...tml-a18bdc03.js => about.html-1efe40f7.js} | 2 +- ...tml-177390a1.js => about.html-b3827bb1.js} | 2 +- assets/app-a9d55428.js | 440 ++++++++++++++++++ assets/app-b649ee34.js | 377 --------------- ...ng-password-in-plaintext.html-7173da3b.js} | 2 +- ...ng-password-in-plaintext.html-e9e5371d.js} | 2 +- ...ow-utf8mb4-and-collation.html-77f7e2a5.js} | 2 +- ...ow-utf8mb4-and-collation.html-d98b859a.js} | 2 +- ... => check-if-name-exists.html-20410c4b.js} | 2 +- ... => check-if-name-exists.html-488fca17.js} | 2 +- ...n-extract-info-from-html.html-47bc02a1.js} | 2 +- ...n-extract-info-from-html.html-cd27a8d3.js} | 2 +- ...tices-for-handling-excel.html-c351dd73.js} | 2 +- ...tices-for-handling-excel.html-d03a1dea.js} | 2 +- ...porary-college-english-1.html-49632c93.js} | 2 +- ...porary-college-english-1.html-e4e094da.js} | 2 +- ...porary-college-english-2.html-47fef5cf.js} | 2 +- ...porary-college-english-2.html-675f1cf2.js} | 2 +- ...porary-college-english-3.html-06118028.js} | 2 +- ...porary-college-english-3.html-80541a07.js} | 2 +- ...porary-college-english-4.html-5a5508d7.js} | 2 +- ...porary-college-english-4.html-756c6e8b.js} | 2 +- ...porary-college-english-5.html-5e519758.js} | 2 +- ...porary-college-english-5.html-c752988e.js} | 2 +- ...porary-college-english-6.html-3f6139cd.js} | 2 +- ...porary-college-english-6.html-cc0d8518.js} | 2 +- ...y-code-may-not-be-guilty.html-52cea58d.js} | 2 +- ...y-code-may-not-be-guilty.html-5562ab70.js} | 2 +- ...t-try-to-argue-with-a-sb.html-5919dbb8.js} | 2 +- ...t-try-to-argue-with-a-sb.html-c2c1581f.js} | 2 +- ...learn-english-1-overview.html-52c2cad5.js} | 2 +- ...learn-english-1-overview.html-a4b93650.js} | 2 +- ...-english-2-pronunciation.html-796320a9.js} | 2 +- ...-english-2-pronunciation.html-f7ca6655.js} | 2 +- ...an-learn-english-3-words.html-c288ffa2.js} | 2 +- ...an-learn-english-3-words.html-d680e9c6.js} | 2 +- ...4-listening-and-speaking.html-8bf4c725.js} | 2 +- ...4-listening-and-speaking.html-bcf756a7.js} | 2 +- ...sh-5-reading-and-writing.html-630438e5.js} | 2 +- ...sh-5-reading-and-writing.html-cf6b1e9b.js} | 2 +- ...t-mysql-table-into-excel.html-3d06cd3e.js} | 2 +- ...t-mysql-table-into-excel.html-e4373920.js} | 2 +- ....js => git-best-pratices.html-4b6532e6.js} | 2 +- ....js => git-best-pratices.html-9f9a9e0e.js} | 2 +- ...tive-guide-to-merge-code.html-88801d1b.js} | 2 +- ...tive-guide-to-merge-code.html-abe98718.js} | 2 +- ...story-two-tricks-in-idea.html-0b65486a.js} | 2 +- ...story-two-tricks-in-idea.html-41e54e71.js} | 2 +- ...s => git-useful-commands.html-3d5dd23d.js} | 2 +- ...s => git-useful-commands.html-517e5b83.js} | 2 +- ...e4938506.js => gitlab-ci.html-01cc0cbe.js} | 2 +- ...2b32bfd2.js => gitlab-ci.html-99056a64.js} | 2 +- ...w-to-connect-to-internet.html-0a649e4e.js} | 2 +- ...w-to-connect-to-internet.html-b65ec9df.js} | 2 +- ...-jar-without-source-code.html-bbf44529.js} | 2 +- ...-jar-without-source-code.html-f9386128.js} | 2 +- ...f-evaluate-english-level.html-16ae55da.js} | 2 +- ...f-evaluate-english-level.html-cc5748d1.js} | 2 +- ...tml-0f5c0f0d.js => index.html-070b1600.js} | 2 +- ...tml-1d3ab01e.js => index.html-112bc49c.js} | 2 +- ...tml-049bd54f.js => index.html-14879732.js} | 2 +- assets/index.html-18a625f7.js | 1 - assets/index.html-1d219a24.js | 1 - assets/index.html-2728cdd7.js | 1 - ...tml-05a6aceb.js => index.html-2a568803.js} | 2 +- assets/index.html-342e7095.js | 1 - ...tml-06bf462d.js => index.html-3a00d6ef.js} | 2 +- assets/index.html-3a97ce45.js | 1 - ...tml-5a646c34.js => index.html-408ce1a1.js} | 2 +- assets/index.html-43e4e5f7.js | 1 + assets/index.html-4458b0dc.js | 1 + ...tml-6d6c9ccc.js => index.html-4c220eec.js} | 2 +- assets/index.html-4d16e2b0.js | 1 + assets/index.html-515fd40f.js | 1 + assets/index.html-55ab9a01.js | 1 + assets/index.html-55bb043c.js | 1 - assets/index.html-5859e69f.js | 1 - assets/index.html-6635a236.js | 1 - assets/index.html-6cb930a5.js | 1 + assets/index.html-7ad6c908.js | 1 + assets/index.html-88e3a8c0.js | 1 + assets/index.html-896d34f9.js | 1 + assets/index.html-8be4c9cb.js | 1 - assets/index.html-8bec8f40.js | 1 + assets/index.html-9070516e.js | 1 - assets/index.html-9122fca1.js | 1 - assets/index.html-91f24fc6.js | 1 + assets/index.html-956353ed.js | 1 - assets/index.html-967c662b.js | 1 - assets/index.html-9890dac2.js | 1 - assets/index.html-98c939cd.js | 1 + assets/index.html-9b7d49c9.js | 1 + ...tml-396a0c75.js => index.html-9c477b31.js} | 2 +- ...tml-86d779f8.js => index.html-9cbf5acb.js} | 2 +- assets/index.html-9fa06e1f.js | 1 + assets/index.html-a0b725d1.js | 1 - assets/index.html-a621f81c.js | 1 + assets/index.html-a6b0d770.js | 1 - assets/index.html-ae84f935.js | 1 + assets/index.html-b4618361.js | 1 - assets/index.html-b5310c3c.js | 1 - assets/index.html-b69b0952.js | 1 - assets/index.html-b895087a.js | 1 + assets/index.html-b9ef174d.js | 1 - assets/index.html-bd2fe08a.js | 1 + assets/index.html-bdcc3d36.js | 1 - assets/index.html-bfd1105e.js | 1 + assets/index.html-c6b00465.js | 1 + assets/index.html-c933b166.js | 1 + assets/index.html-d70e9d37.js | 1 + assets/index.html-e013b2c1.js | 1 - assets/index.html-e53262f0.js | 1 + assets/index.html-e76096f8.js | 1 + assets/index.html-e7a8926e.js | 1 + assets/index.html-e9e56fc9.js | 1 - assets/index.html-f1f01e82.js | 1 - assets/index.html-f4939e44.js | 1 - assets/index.html-fb514775.js | 1 + assets/index.html-feab3d86.js | 1 - ...retrospective-of-sanyuan.html-8d424c1c.js} | 2 +- ...retrospective-of-sanyuan.html-b44a88d2.js} | 2 +- ...000-words-task-completed.html-c7c6176d.js} | 2 +- ...000-words-task-completed.html-f60d77c6.js} | 2 +- ...foreign-language-teacher.html-18abfed2.js} | 2 +- ...foreign-language-teacher.html-4287d21b.js} | 2 +- ...oost-coding-productivity.html-0894834e.js} | 2 +- ...oost-coding-productivity.html-77d0fee4.js} | 2 +- ...tudy-mysqldump-in-action.html-73cda0a5.js} | 2 +- ...tudy-mysqldump-in-action.html-b5eeab0a.js} | 2 +- ...-study-add-auto-increment.html-6018956d.js | 1 + ...-study-add-auto-increment.html-75552c95.js | 1 - ...study-add-auto-increment.html-b3a99eec.js} | 4 +- ...cute-sql-in-command-line.html-3a6f2dc3.js} | 2 +- ...cute-sql-in-command-line.html-e11c76db.js} | 2 +- ...d9bf0.js => old-articles.html-6079e753.js} | 2 +- ...1ffdc.js => old-articles.html-ec567119.js} | 2 +- ...e-optimization-in-action.html-0aff1283.js} | 2 +- ...e-optimization-in-action.html-fa684067.js} | 2 +- ...ections-naming-convention.html-4ce11630.js | 46 ++ ...ections-naming-convention.html-ba39413b.js | 1 + ...-for-query-by-date-range.html-c4cb8fd3.js} | 2 +- ...-for-query-by-date-range.html-f4c04744.js} | 2 +- ...or-writing-good-functions.html-263e2d20.js | 62 +++ ...or-writing-good-functions.html-6cc4ed1e.js | 1 + ...y-cto-of-microsoft-china.html-49fb2b4d.js} | 2 +- ...y-cto-of-microsoft-china.html-66a9c3f3.js} | 2 +- ...s => rethinking-git-flow.html-60cc8caf.js} | 2 +- ...s => rethinking-git-flow.html-cd22e8fc.js} | 2 +- ...h-production-environments.html-009cb1c4.js | 1 + ...h-production-environments.html-292c2a25.js | 2 + ...I-have-to-vent-about-vue.html-44f6a97e.js} | 2 +- ...I-have-to-vent-about-vue.html-9c4bcda2.js} | 2 +- ...=> unit-testing-overview.html-3f5f616d.js} | 2 +- ...=> unit-testing-overview.html-c6b939b4.js} | 2 +- ...tAssured-for-api-testing.html-a06e24bd.js} | 2 +- ...tAssured-for-api-testing.html-db3b5b16.js} | 2 +- ...aude2-instead-of-chatgpt.html-c55c251a.js} | 2 +- ...aude2-instead-of-chatgpt.html-d4f72795.js} | 2 +- ...age-gitlab-merge-request.html-1f237536.js} | 2 +- ...age-gitlab-merge-request.html-e01c2260.js} | 2 +- ...-cypress-for-e2e-testing.html-2b65285a.js} | 2 +- ...-cypress-for-e2e-testing.html-450ffdcd.js} | 2 +- ...-test-driven-development.html-d2d469d3.js} | 2 +- ...-test-driven-development.html-ed10dede.js} | 2 +- ...laywright-for-ui-testing.html-f3b691a5.js} | 2 +- ...laywright-for-ui-testing.html-fba57f32.js} | 2 +- ...-postman-for-api-testing.html-e4659ca6.js} | 2 +- ...-postman-for-api-testing.html-ed796a62.js} | 9 +- assets/using-enum-in-java.html-cb86be54.js | 74 +++ assets/using-enum-in-java.html-db9b25e7.js | 1 + ...=> vim-creator-pass-away.html-89c669cf.js} | 2 +- ...=> vim-creator-pass-away.html-c4a8f7ca.js} | 2 +- ...ence-between-sh-and-bash.html-38f57eba.js} | 2 +- ...ence-between-sh-and-bash.html-f59545ff.js} | 2 +- ...better-Boolean-or-boolean.html-03e8b2fa.js | 1 + ...better-Boolean-or-boolean.html-90421d51.js | 16 + ...-is-better-forEach-or-map.html-16d69e48.js | 1 + ...-is-better-forEach-or-map.html-7837b1cd.js | 71 +++ ...tjson-instead-of-jackson.html-67cee556.js} | 2 +- ...tjson-instead-of-jackson.html-ef0d6e0e.js} | 2 +- ...-to-upgrade-dependencies.html-8df8757e.js} | 2 +- ...-to-upgrade-dependencies.html-996256d6.js} | 2 +- ...tenant_id-to-every-table.html-36585552.js} | 2 +- ...tenant_id-to-every-table.html-d78e0fcf.js} | 2 +- category/index.html | 8 +- daily/a-vuepress2-entertaining-video.html | 8 +- daily/a-warning-from-navicat.html | 8 +- daily/about-arm-things-you-need-to-know.html | 8 +- ...tf8-do-you-know-utf8mb4-and-collation.html | 8 +- ...e-ai-in-action-extract-info-from-html.html | 8 +- daily/copy-code-may-not-be-guilty.html | 8 +- daily/dont-try-to-argue-with-a-sb.html | 8 +- daily/index.html | 6 +- daily/iteration-retrospective-of-sanyuan.html | 8 +- ...erage-ai-to-boost-coding-productivity.html | 8 +- ...on-a-speech-by-cto-of-microsoft-china.html | 8 +- ...nsistent-with-production-environments.html | 44 ++ daily/things-I-have-to-vent-about-vue.html | 8 +- daily/use-claude2-instead-of-chatgpt.html | 8 +- daily/vim-creator-pass-away.html | 8 +- ...is-the-difference-between-sh-and-bash.html | 8 +- ...-need-to-add-tenant_id-to-every-table.html | 8 +- english/contemporary-college-english-1.html | 6 +- english/contemporary-college-english-2.html | 6 +- english/contemporary-college-english-3.html | 6 +- english/contemporary-college-english-4.html | 6 +- english/contemporary-college-english-5.html | 6 +- english/contemporary-college-english-6.html | 6 +- ...everyone-can-learn-english-1-overview.html | 6 +- ...one-can-learn-english-2-pronunciation.html | 6 +- .../everyone-can-learn-english-3-words.html | 6 +- ...earn-english-4-listening-and-speaking.html | 6 +- ...n-learn-english-5-reading-and-writing.html | 6 +- .../how-to-self-evaluate-english-level.html | 6 +- english/index.html | 4 +- .../learning-7000-words-task-completed.html | 6 +- ...tgpt-be-your-foreign-language-teacher.html | 6 +- frontend/index.html | 6 +- frontend/old-articles.html | 8 +- .../performance-optimization-in-action.html | 8 +- git/git-best-pratices.html | 8 +- git/git-definitive-guide-to-merge-code.html | 8 +- git/git-history-two-tricks-in-idea.html | 8 +- git/git-useful-commands.html | 8 +- git/gitlab-ci.html | 8 +- git/index.html | 6 +- git/rethinking-git-flow.html | 8 +- ...e-tool-to-manage-gitlab-merge-request.html | 8 +- index.html | 70 +-- ...ving-Common-Problems-in-IntelliJ-IDEA.html | 8 +- ...Resolving-Common-Problems-in-Maven.md.html | 10 +- java/avoid-sending-password-in-plaintext.html | 10 +- java/check-if-name-exists.html | 8 +- java/common-practices-for-handling-excel.html | 8 +- ...-into-release-jar-without-source-code.html | 8 +- java/index.html | 6 +- ...ces-for-collections-naming-convention.html | 88 ++++ ...end-practices-for-query-by-date-range.html | 8 +- ...-practices-for-writing-good-functions.html | 104 +++++ java/using-enum-in-java.html | 116 +++++ ...hich-one-is-better-Boolean-or-boolean.html | 58 +++ java/which-one-is-better-forEach-or-map.html | 113 +++++ ...-i-prefer-fastjson-instead-of-jackson.html | 8 +- ...is-it-so-hard-to-upgrade-dependencies.html | 8 +- mysql/index.html | 6 +- ...backup-case-study-mysqldump-in-action.html | 8 +- ...gration-case-study-add-auto-increment.html | 12 +- ...know-when-execute-sql-in-command-line.html | 8 +- python/export-mysql-table-into-excel.html | 8 +- python/index.html | 6 +- rss.xml | 414 +++++++++++++++- sitemap.xml | 2 +- software-testing/index.html | 6 +- software-testing/unit-testing-overview.html | 8 +- .../use-RestAssured-for-api-testing.html | 8 +- .../use-cypress-for-e2e-testing.html | 8 +- .../use-jest-for-test-driven-development.html | 8 +- .../use-playwright-for-ui-testing.html | 8 +- .../use-postman-for-api-testing.html | 13 +- star/index.html | 8 +- tag/ai/index.html | 8 +- tag/daily/index.html | 39 +- tag/design/index.html | 8 +- tag/docker/index.html | 8 +- tag/emotion/index.html | 8 +- tag/english/index.html | 8 +- tag/frontend/index.html | 8 +- tag/git/index.html | 8 +- tag/gitlab/index.html | 8 +- tag/index.html | 8 +- tag/java/index.html | 8 +- tag/javascript/index.html | 8 +- tag/linux/index.html | 8 +- tag/mysql/index.html | 15 +- tag/node.js/index.html | 10 +- tag/python/index.html | 8 +- tag/testing/index.html | 8 +- tag/tool/index.html | 8 +- tag/video/index.html | 8 +- tag/working-experience/index.html | 8 +- timeline/index.html | 8 +- tools/how-to-connect-to-internet.html | 8 +- tools/index.html | 6 +- 297 files changed, 2278 insertions(+), 949 deletions(-) rename assets/{404.html-fc95297b.js => 404.html-0b613605.js} (71%) rename assets/{Resolving-Common-Problems-in-IntelliJ-IDEA.html-4d83e594.js => Resolving-Common-Problems-in-IntelliJ-IDEA.html-73ad8a4f.js} (92%) rename assets/{Resolving-Common-Problems-in-IntelliJ-IDEA.html-052f4669.js => Resolving-Common-Problems-in-IntelliJ-IDEA.html-ce1eab94.js} (98%) rename assets/{Resolving-Common-Problems-in-Maven.md.html-baa4321e.js => Resolving-Common-Problems-in-Maven.md.html-9fd731f4.js} (99%) rename assets/{Resolving-Common-Problems-in-Maven.md.html-4d4645f7.js => Resolving-Common-Problems-in-Maven.md.html-f77903d5.js} (91%) rename assets/{a-vuepress2-entertaining-video.html-348ce561.js => a-vuepress2-entertaining-video.html-c410f2a7.js} (81%) rename assets/{a-vuepress2-entertaining-video.html-11220e81.js => a-vuepress2-entertaining-video.html-d7d94634.js} (89%) rename assets/{a-warning-from-navicat.html-159d00a7.js => a-warning-from-navicat.html-d1243c4d.js} (84%) rename assets/{a-warning-from-navicat.html-1f409a81.js => a-warning-from-navicat.html-dbede3aa.js} (91%) rename assets/{about-arm-things-you-need-to-know.html-0feee41c.js => about-arm-things-you-need-to-know.html-a719ca33.js} (92%) rename assets/{about-arm-things-you-need-to-know.html-ce8055c2.js => about-arm-things-you-need-to-know.html-b26fd653.js} (85%) rename assets/{about.html-a18bdc03.js => about.html-1efe40f7.js} (90%) rename assets/{about.html-177390a1.js => about.html-b3827bb1.js} (86%) create mode 100644 assets/app-a9d55428.js delete mode 100644 assets/app-b649ee34.js rename assets/{avoid-sending-password-in-plaintext.html-6233e8a7.js => avoid-sending-password-in-plaintext.html-7173da3b.js} (91%) rename assets/{avoid-sending-password-in-plaintext.html-43f53b8a.js => avoid-sending-password-in-plaintext.html-e9e5371d.js} (99%) rename assets/{beyond-utf8-do-you-know-utf8mb4-and-collation.html-5673c414.js => beyond-utf8-do-you-know-utf8mb4-and-collation.html-77f7e2a5.js} (93%) rename assets/{beyond-utf8-do-you-know-utf8mb4-and-collation.html-85d76568.js => beyond-utf8-do-you-know-utf8mb4-and-collation.html-d98b859a.js} (99%) rename assets/{check-if-name-exists.html-4b9b7d86.js => check-if-name-exists.html-20410c4b.js} (88%) rename assets/{check-if-name-exists.html-abc8b8dd.js => check-if-name-exists.html-488fca17.js} (99%) rename assets/{claude-ai-in-action-extract-info-from-html.html-2568b3bd.js => claude-ai-in-action-extract-info-from-html.html-47bc02a1.js} (82%) rename assets/{claude-ai-in-action-extract-info-from-html.html-2c1451df.js => claude-ai-in-action-extract-info-from-html.html-cd27a8d3.js} (89%) rename assets/{common-practices-for-handling-excel.html-0efdabf9.js => common-practices-for-handling-excel.html-c351dd73.js} (93%) rename assets/{common-practices-for-handling-excel.html-82289612.js => common-practices-for-handling-excel.html-d03a1dea.js} (99%) rename assets/{contemporary-college-english-1.html-6b25fa3f.js => contemporary-college-english-1.html-49632c93.js} (91%) rename assets/{contemporary-college-english-1.html-8191f95a.js => contemporary-college-english-1.html-e4e094da.js} (98%) rename assets/{contemporary-college-english-2.html-82c3bbbf.js => contemporary-college-english-2.html-47fef5cf.js} (98%) rename assets/{contemporary-college-english-2.html-de9e2dd5.js => contemporary-college-english-2.html-675f1cf2.js} (92%) rename assets/{contemporary-college-english-3.html-9d16468c.js => contemporary-college-english-3.html-06118028.js} (93%) rename assets/{contemporary-college-english-3.html-f1b95697.js => contemporary-college-english-3.html-80541a07.js} (98%) rename assets/{contemporary-college-english-4.html-ab55347b.js => contemporary-college-english-4.html-5a5508d7.js} (90%) rename assets/{contemporary-college-english-4.html-bc05713f.js => contemporary-college-english-4.html-756c6e8b.js} (98%) rename assets/{contemporary-college-english-5.html-b75c916f.js => contemporary-college-english-5.html-5e519758.js} (99%) rename assets/{contemporary-college-english-5.html-461e5519.js => contemporary-college-english-5.html-c752988e.js} (94%) rename assets/{contemporary-college-english-6.html-cadc7c30.js => contemporary-college-english-6.html-3f6139cd.js} (98%) rename assets/{contemporary-college-english-6.html-b05e70fd.js => contemporary-college-english-6.html-cc0d8518.js} (93%) rename assets/{copy-code-may-not-be-guilty.html-765412c3.js => copy-code-may-not-be-guilty.html-52cea58d.js} (91%) rename assets/{copy-code-may-not-be-guilty.html-6eb3011e.js => copy-code-may-not-be-guilty.html-5562ab70.js} (97%) rename assets/{dont-try-to-argue-with-a-sb.html-6b8dcf17.js => dont-try-to-argue-with-a-sb.html-5919dbb8.js} (98%) rename assets/{dont-try-to-argue-with-a-sb.html-71bbf5fd.js => dont-try-to-argue-with-a-sb.html-c2c1581f.js} (90%) rename assets/{everyone-can-learn-english-1-overview.html-f280cd0b.js => everyone-can-learn-english-1-overview.html-52c2cad5.js} (92%) rename assets/{everyone-can-learn-english-1-overview.html-50a1c72e.js => everyone-can-learn-english-1-overview.html-a4b93650.js} (98%) rename assets/{everyone-can-learn-english-2-pronunciation.html-991c3fa5.js => everyone-can-learn-english-2-pronunciation.html-796320a9.js} (97%) rename assets/{everyone-can-learn-english-2-pronunciation.html-3bc03470.js => everyone-can-learn-english-2-pronunciation.html-f7ca6655.js} (92%) rename assets/{everyone-can-learn-english-3-words.html-6f098cd3.js => everyone-can-learn-english-3-words.html-c288ffa2.js} (90%) rename assets/{everyone-can-learn-english-3-words.html-10909866.js => everyone-can-learn-english-3-words.html-d680e9c6.js} (98%) rename assets/{everyone-can-learn-english-4-listening-and-speaking.html-37f330d6.js => everyone-can-learn-english-4-listening-and-speaking.html-8bf4c725.js} (98%) rename assets/{everyone-can-learn-english-4-listening-and-speaking.html-e5671ccf.js => everyone-can-learn-english-4-listening-and-speaking.html-bcf756a7.js} (94%) rename assets/{everyone-can-learn-english-5-reading-and-writing.html-3815eb16.js => everyone-can-learn-english-5-reading-and-writing.html-630438e5.js} (95%) rename assets/{everyone-can-learn-english-5-reading-and-writing.html-63730d51.js => everyone-can-learn-english-5-reading-and-writing.html-cf6b1e9b.js} (99%) rename assets/{export-mysql-table-into-excel.html-9bc68678.js => export-mysql-table-into-excel.html-3d06cd3e.js} (89%) rename assets/{export-mysql-table-into-excel.html-6db61b30.js => export-mysql-table-into-excel.html-e4373920.js} (99%) rename assets/{git-best-pratices.html-73d0401b.js => git-best-pratices.html-4b6532e6.js} (98%) rename assets/{git-best-pratices.html-4037825e.js => git-best-pratices.html-9f9a9e0e.js} (93%) rename assets/{git-definitive-guide-to-merge-code.html-06d295be.js => git-definitive-guide-to-merge-code.html-88801d1b.js} (93%) rename assets/{git-definitive-guide-to-merge-code.html-39271ae4.js => git-definitive-guide-to-merge-code.html-abe98718.js} (99%) rename assets/{git-history-two-tricks-in-idea.html-fd760def.js => git-history-two-tricks-in-idea.html-0b65486a.js} (87%) rename assets/{git-history-two-tricks-in-idea.html-3b5a7689.js => git-history-two-tricks-in-idea.html-41e54e71.js} (98%) rename assets/{git-useful-commands.html-2e056050.js => git-useful-commands.html-3d5dd23d.js} (95%) rename assets/{git-useful-commands.html-17a54d4f.js => git-useful-commands.html-517e5b83.js} (99%) rename assets/{gitlab-ci.html-e4938506.js => gitlab-ci.html-01cc0cbe.js} (99%) rename assets/{gitlab-ci.html-2b32bfd2.js => gitlab-ci.html-99056a64.js} (92%) rename assets/{how-to-connect-to-internet.html-30b7db89.js => how-to-connect-to-internet.html-0a649e4e.js} (93%) rename assets/{how-to-connect-to-internet.html-994bb0e3.js => how-to-connect-to-internet.html-b65ec9df.js} (98%) rename assets/{how-to-convert-snapshot-into-release-jar-without-source-code.html-77c8640b.js => how-to-convert-snapshot-into-release-jar-without-source-code.html-bbf44529.js} (98%) rename assets/{how-to-convert-snapshot-into-release-jar-without-source-code.html-66363334.js => how-to-convert-snapshot-into-release-jar-without-source-code.html-f9386128.js} (92%) rename assets/{how-to-self-evaluate-english-level.html-bc3d8f29.js => how-to-self-evaluate-english-level.html-16ae55da.js} (97%) rename assets/{how-to-self-evaluate-english-level.html-61f99657.js => how-to-self-evaluate-english-level.html-cc5748d1.js} (93%) rename assets/{index.html-0f5c0f0d.js => index.html-070b1600.js} (71%) rename assets/{index.html-1d3ab01e.js => index.html-112bc49c.js} (76%) rename assets/{index.html-049bd54f.js => index.html-14879732.js} (71%) delete mode 100644 assets/index.html-18a625f7.js delete mode 100644 assets/index.html-1d219a24.js delete mode 100644 assets/index.html-2728cdd7.js rename assets/{index.html-05a6aceb.js => index.html-2a568803.js} (71%) delete mode 100644 assets/index.html-342e7095.js rename assets/{index.html-06bf462d.js => index.html-3a00d6ef.js} (71%) delete mode 100644 assets/index.html-3a97ce45.js rename assets/{index.html-5a646c34.js => index.html-408ce1a1.js} (76%) create mode 100644 assets/index.html-43e4e5f7.js create mode 100644 assets/index.html-4458b0dc.js rename assets/{index.html-6d6c9ccc.js => index.html-4c220eec.js} (76%) create mode 100644 assets/index.html-4d16e2b0.js create mode 100644 assets/index.html-515fd40f.js create mode 100644 assets/index.html-55ab9a01.js delete mode 100644 assets/index.html-55bb043c.js delete mode 100644 assets/index.html-5859e69f.js delete mode 100644 assets/index.html-6635a236.js create mode 100644 assets/index.html-6cb930a5.js create mode 100644 assets/index.html-7ad6c908.js create mode 100644 assets/index.html-88e3a8c0.js create mode 100644 assets/index.html-896d34f9.js delete mode 100644 assets/index.html-8be4c9cb.js create mode 100644 assets/index.html-8bec8f40.js delete mode 100644 assets/index.html-9070516e.js delete mode 100644 assets/index.html-9122fca1.js create mode 100644 assets/index.html-91f24fc6.js delete mode 100644 assets/index.html-956353ed.js delete mode 100644 assets/index.html-967c662b.js delete mode 100644 assets/index.html-9890dac2.js create mode 100644 assets/index.html-98c939cd.js create mode 100644 assets/index.html-9b7d49c9.js rename assets/{index.html-396a0c75.js => index.html-9c477b31.js} (68%) rename assets/{index.html-86d779f8.js => index.html-9cbf5acb.js} (76%) create mode 100644 assets/index.html-9fa06e1f.js delete mode 100644 assets/index.html-a0b725d1.js create mode 100644 assets/index.html-a621f81c.js delete mode 100644 assets/index.html-a6b0d770.js create mode 100644 assets/index.html-ae84f935.js delete mode 100644 assets/index.html-b4618361.js delete mode 100644 assets/index.html-b5310c3c.js delete mode 100644 assets/index.html-b69b0952.js create mode 100644 assets/index.html-b895087a.js delete mode 100644 assets/index.html-b9ef174d.js create mode 100644 assets/index.html-bd2fe08a.js delete mode 100644 assets/index.html-bdcc3d36.js create mode 100644 assets/index.html-bfd1105e.js create mode 100644 assets/index.html-c6b00465.js create mode 100644 assets/index.html-c933b166.js create mode 100644 assets/index.html-d70e9d37.js delete mode 100644 assets/index.html-e013b2c1.js create mode 100644 assets/index.html-e53262f0.js create mode 100644 assets/index.html-e76096f8.js create mode 100644 assets/index.html-e7a8926e.js delete mode 100644 assets/index.html-e9e56fc9.js delete mode 100644 assets/index.html-f1f01e82.js delete mode 100644 assets/index.html-f4939e44.js create mode 100644 assets/index.html-fb514775.js delete mode 100644 assets/index.html-feab3d86.js rename assets/{iteration-retrospective-of-sanyuan.html-27cf4da2.js => iteration-retrospective-of-sanyuan.html-8d424c1c.js} (98%) rename assets/{iteration-retrospective-of-sanyuan.html-9a8c4533.js => iteration-retrospective-of-sanyuan.html-b44a88d2.js} (89%) rename assets/{learning-7000-words-task-completed.html-8232d487.js => learning-7000-words-task-completed.html-c7c6176d.js} (88%) rename assets/{learning-7000-words-task-completed.html-55872900.js => learning-7000-words-task-completed.html-f60d77c6.js} (98%) rename assets/{let-chatgpt-be-your-foreign-language-teacher.html-3ed89775.js => let-chatgpt-be-your-foreign-language-teacher.html-18abfed2.js} (94%) rename assets/{let-chatgpt-be-your-foreign-language-teacher.html-646de3af.js => let-chatgpt-be-your-foreign-language-teacher.html-4287d21b.js} (99%) rename assets/{leverage-ai-to-boost-coding-productivity.html-fac3783c.js => leverage-ai-to-boost-coding-productivity.html-0894834e.js} (90%) rename assets/{leverage-ai-to-boost-coding-productivity.html-b3dab214.js => leverage-ai-to-boost-coding-productivity.html-77d0fee4.js} (99%) rename assets/{mysql-backup-case-study-mysqldump-in-action.html-cea2b606.js => mysql-backup-case-study-mysqldump-in-action.html-73cda0a5.js} (90%) rename assets/{mysql-backup-case-study-mysqldump-in-action.html-27652577.js => mysql-backup-case-study-mysqldump-in-action.html-b5eeab0a.js} (99%) create mode 100644 assets/mysql-data-migration-case-study-add-auto-increment.html-6018956d.js delete mode 100644 assets/mysql-data-migration-case-study-add-auto-increment.html-75552c95.js rename assets/{mysql-data-migration-case-study-add-auto-increment.html-0e5bf45b.js => mysql-data-migration-case-study-add-auto-increment.html-b3a99eec.js} (90%) rename assets/{mysql-details-you-should-know-when-execute-sql-in-command-line.html-9a9e576f.js => mysql-details-you-should-know-when-execute-sql-in-command-line.html-3a6f2dc3.js} (91%) rename assets/{mysql-details-you-should-know-when-execute-sql-in-command-line.html-834b3dd8.js => mysql-details-you-should-know-when-execute-sql-in-command-line.html-e11c76db.js} (98%) rename assets/{old-articles.html-3e4d9bf0.js => old-articles.html-6079e753.js} (92%) rename assets/{old-articles.html-3441ffdc.js => old-articles.html-ec567119.js} (96%) rename assets/{performance-optimization-in-action.html-f6072ca8.js => performance-optimization-in-action.html-0aff1283.js} (91%) rename assets/{performance-optimization-in-action.html-963023b4.js => performance-optimization-in-action.html-fa684067.js} (99%) create mode 100644 assets/recommend-practices-for-collections-naming-convention.html-4ce11630.js create mode 100644 assets/recommend-practices-for-collections-naming-convention.html-ba39413b.js rename assets/{recommend-practices-for-query-by-date-range.html-9cb3e939.js => recommend-practices-for-query-by-date-range.html-c4cb8fd3.js} (90%) rename assets/{recommend-practices-for-query-by-date-range.html-c58f48b5.js => recommend-practices-for-query-by-date-range.html-f4c04744.js} (99%) create mode 100644 assets/recommend-practices-for-writing-good-functions.html-263e2d20.js create mode 100644 assets/recommend-practices-for-writing-good-functions.html-6cc4ed1e.js rename assets/{reflections-on-a-speech-by-cto-of-microsoft-china.html-32094be4.js => reflections-on-a-speech-by-cto-of-microsoft-china.html-49fb2b4d.js} (81%) rename assets/{reflections-on-a-speech-by-cto-of-microsoft-china.html-51d46984.js => reflections-on-a-speech-by-cto-of-microsoft-china.html-66a9c3f3.js} (85%) rename assets/{rethinking-git-flow.html-39a67167.js => rethinking-git-flow.html-60cc8caf.js} (90%) rename assets/{rethinking-git-flow.html-2dbc1f93.js => rethinking-git-flow.html-cd22e8fc.js} (99%) create mode 100644 assets/testing-environments-should-be-consistent-with-production-environments.html-009cb1c4.js create mode 100644 assets/testing-environments-should-be-consistent-with-production-environments.html-292c2a25.js rename assets/{things-I-have-to-vent-about-vue.html-862aa8df.js => things-I-have-to-vent-about-vue.html-44f6a97e.js} (93%) rename assets/{things-I-have-to-vent-about-vue.html-38ebc534.js => things-I-have-to-vent-about-vue.html-9c4bcda2.js} (91%) rename assets/{unit-testing-overview.html-aab524e2.js => unit-testing-overview.html-3f5f616d.js} (99%) rename assets/{unit-testing-overview.html-9597f90a.js => unit-testing-overview.html-c6b939b4.js} (89%) rename assets/{use-RestAssured-for-api-testing.html-b8869f75.js => use-RestAssured-for-api-testing.html-a06e24bd.js} (93%) rename assets/{use-RestAssured-for-api-testing.html-badc2513.js => use-RestAssured-for-api-testing.html-db3b5b16.js} (99%) rename assets/{use-claude2-instead-of-chatgpt.html-cc6aebc1.js => use-claude2-instead-of-chatgpt.html-c55c251a.js} (99%) rename assets/{use-claude2-instead-of-chatgpt.html-a143e23e.js => use-claude2-instead-of-chatgpt.html-d4f72795.js} (84%) rename assets/{use-command-line-tool-to-manage-gitlab-merge-request.html-4c25576d.js => use-command-line-tool-to-manage-gitlab-merge-request.html-1f237536.js} (95%) rename assets/{use-command-line-tool-to-manage-gitlab-merge-request.html-bb0e24b0.js => use-command-line-tool-to-manage-gitlab-merge-request.html-e01c2260.js} (99%) rename assets/{use-cypress-for-e2e-testing.html-a3aed518.js => use-cypress-for-e2e-testing.html-2b65285a.js} (93%) rename assets/{use-cypress-for-e2e-testing.html-1c183bce.js => use-cypress-for-e2e-testing.html-450ffdcd.js} (99%) rename assets/{use-jest-for-test-driven-development.html-842be257.js => use-jest-for-test-driven-development.html-d2d469d3.js} (89%) rename assets/{use-jest-for-test-driven-development.html-8fbc9f0a.js => use-jest-for-test-driven-development.html-ed10dede.js} (99%) rename assets/{use-playwright-for-ui-testing.html-44356ace.js => use-playwright-for-ui-testing.html-f3b691a5.js} (99%) rename assets/{use-playwright-for-ui-testing.html-39747b36.js => use-playwright-for-ui-testing.html-fba57f32.js} (94%) rename assets/{use-postman-for-api-testing.html-69020f9d.js => use-postman-for-api-testing.html-e4659ca6.js} (93%) rename assets/{use-postman-for-api-testing.html-557f4cb8.js => use-postman-for-api-testing.html-ed796a62.js} (92%) create mode 100644 assets/using-enum-in-java.html-cb86be54.js create mode 100644 assets/using-enum-in-java.html-db9b25e7.js rename assets/{vim-creator-pass-away.html-c6774dfb.js => vim-creator-pass-away.html-89c669cf.js} (91%) rename assets/{vim-creator-pass-away.html-1941f40c.js => vim-creator-pass-away.html-c4a8f7ca.js} (83%) rename assets/{what-is-the-difference-between-sh-and-bash.html-c3e619d0.js => what-is-the-difference-between-sh-and-bash.html-38f57eba.js} (84%) rename assets/{what-is-the-difference-between-sh-and-bash.html-41ae2768.js => what-is-the-difference-between-sh-and-bash.html-f59545ff.js} (94%) create mode 100644 assets/which-one-is-better-Boolean-or-boolean.html-03e8b2fa.js create mode 100644 assets/which-one-is-better-Boolean-or-boolean.html-90421d51.js create mode 100644 assets/which-one-is-better-forEach-or-map.html-16d69e48.js create mode 100644 assets/which-one-is-better-forEach-or-map.html-7837b1cd.js rename assets/{why-i-prefer-fastjson-instead-of-jackson.html-f1819a15.js => why-i-prefer-fastjson-instead-of-jackson.html-67cee556.js} (92%) rename assets/{why-i-prefer-fastjson-instead-of-jackson.html-3961785e.js => why-i-prefer-fastjson-instead-of-jackson.html-ef0d6e0e.js} (97%) rename assets/{why-is-it-so-hard-to-upgrade-dependencies.html-08962984.js => why-is-it-so-hard-to-upgrade-dependencies.html-8df8757e.js} (98%) rename assets/{why-is-it-so-hard-to-upgrade-dependencies.html-f4295ff8.js => why-is-it-so-hard-to-upgrade-dependencies.html-996256d6.js} (88%) rename assets/{you-dont-need-to-add-tenant_id-to-every-table.html-d1b8df99.js => you-dont-need-to-add-tenant_id-to-every-table.html-36585552.js} (98%) rename assets/{you-dont-need-to-add-tenant_id-to-every-table.html-fadcd1b3.js => you-dont-need-to-add-tenant_id-to-every-table.html-d78e0fcf.js} (86%) create mode 100644 daily/testing-environments-should-be-consistent-with-production-environments.html create mode 100644 java/recommend-practices-for-collections-naming-convention.html create mode 100644 java/recommend-practices-for-writing-good-functions.html create mode 100644 java/using-enum-in-java.html create mode 100644 java/which-one-is-better-Boolean-or-boolean.html create mode 100644 java/which-one-is-better-forEach-or-map.html diff --git a/404.html b/404.html index c201e15b..9ade4311 100644 --- a/404.html +++ b/404.html @@ -34,10 +34,10 @@ } - + -
跳至主要內容
- +
跳至主要內容
+ diff --git a/about.html b/about.html index d6afa1f5..93001d88 100644 --- a/about.html +++ b/about.html @@ -5,7 +5,7 @@ - 关于 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容
- +
跳至主要內容
+ diff --git a/article/index.html b/article/index.html index 3506826b..0fb91815 100644 --- a/article/index.html +++ b/article/index.html @@ -34,10 +34,47 @@ } - + -
跳至主要內容
避免密码明文传输

避免密码明文传输

+
跳至主要內容
Boolean 还是 boolean?

Boolean 还是 boolean?

+

在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢?

+

结论

+

先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。

+

原文如下:
+image.png


levy大约 4 分钟
forEach 还是 map?

forEach 还是 map?

+

背景

+

遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有:

+
List<Type> result = new ArrayList<>();
+list.forEach(src -> {
+    Type target = BeanUtils.copyProperties(src, target);
+    //省略代码
+    result.add(target);
+});
+

levy大约 4 分钟
生产教训:测试环境要与生产环境一致

生产教训:测试环境要与生产环境一致

+

事件还原

+

业务流程:

+
    +
  1. app-a 上传文件
  2. +
  3. app-b 下载文件后使用文件
  4. +
+

其他信息:

+
    +
  1. 开发、测试环境使用 MinIO
  2. +
  3. 生产环境使用 Amazon S3
  4. +
+

问题:

+
    +
  1. app-a 上传文件成功
  2. +
  3. app-b 使用文件报错
  4. +
+

逐步分析定位问题:

+
    +
  1. app-a 与 app-b 配置是否一致?——确认都是使用 S3
  2. +
  3. S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题
  4. +
  5. app-a 是否真的上传成功?——确认文件已在 S3
  6. +
  7. app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。
  8. +

levy大约 2 分钟Daily
避免密码明文传输

避免密码明文传输

说明

密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。

本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。

@@ -61,34 +98,15 @@

前言

虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。

而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。

还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:

-

levy大约 5 分钟Node.jsDaily
根据时间范围查询推荐实践

根据时间范围查询推荐实践

背景

不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。

虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。

本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson


levy大约 3 分钟JavaDaily
微软中国CTO演讲观后感

微软中国CTO演讲观后感

看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。

-

levy小于 1 分钟DailyVideo
迭代复盘之三员管理

迭代复盘之三员管理

-

前言

-

本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。

-

这次迭代因为各种原因,延期了快一个星期(周六还加了班)。

-

那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。

-

levy大约 3 分钟DailyWorking Experience
Excel处理常用实践

Excel处理常用实践

-

基础知识

-

导入需要用到对象,MultipartFile。

-
@PostMapping("/import")
-public boolean importLicense(
-      @RequestParam("file") MultipartFile file,
-      @RequestParam("tenantId") @NotBlank String tenantId,
-) {
-  return true;
-}
-

levy大约 5 分钟JavaDaily
都什么年代了,还在用传统方式写代码?

都什么年代了,还在用传统方式写代码?

-

前言

-

还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

-

本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

-

本文将分享 AI 时代的编程新实践,教你如何从一个 "Ctrl + C"、 "Ctrl + V" 工程师,变成一个 "Tab + Enter" 工程师🤣。


levy大约 7 分钟AIDaily
2
3
4
5
6
- +

levy小于 1 分钟DailyVideo
2
3
4
5
...
7
+ diff --git a/assets/404.html-fc95297b.js b/assets/404.html-0b613605.js similarity index 71% rename from assets/404.html-fc95297b.js rename to assets/404.html-0b613605.js index edd73f10..236832fd 100644 --- a/assets/404.html-fc95297b.js +++ b/assets/404.html-0b613605.js @@ -1 +1 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(_,n){return t(),c("div")}const f=e(o,[["render",r],["__file","404.html.vue"]]);export{f as default}; +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(_,n){return t(),c("div")}const f=e(o,[["render",r],["__file","404.html.vue"]]);export{f as default}; diff --git a/assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-4d83e594.js b/assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-73ad8a4f.js similarity index 92% rename from assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-4d83e594.js rename to assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-73ad8a4f.js index 9baa838a..5929349c 100644 --- a/assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-4d83e594.js +++ b/assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-73ad8a4f.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-86a15cb6","path":"/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html","title":"IDEA常见问题与解决方案","lang":"zh-CN","frontmatter":{"date":"2022-11-25T00:00:00.000Z","tag":["Java","Daily"],"description":"IDEA常见问题与解决方案 启动参数过长 Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun. 解决方案: 编辑 .idea/workspace.xml 找到 PropertiesComponent 添加: 或者这样:","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"IDEA常见问题与解决方案"}],["meta",{"property":"og:description","content":"IDEA常见问题与解决方案 启动参数过长 Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun. 解决方案: 编辑 .idea/workspace.xml 找到 PropertiesComponent 添加: 或者这样:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-11-25T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"IDEA常见问题与解决方案\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-11-25T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"启动参数过长","slug":"启动参数过长","link":"#启动参数过长","children":[]},{"level":2,"title":"设置JDK版本","slug":"设置jdk版本","link":"#设置jdk版本","children":[]},{"level":2,"title":"lombok 编译报错","slug":"lombok-编译报错","link":"#lombok-编译报错","children":[]},{"level":2,"title":"设置启动参数","slug":"设置启动参数","link":"#设置启动参数","children":[]},{"level":2,"title":"栈溢出","slug":"栈溢出","link":"#栈溢出","children":[]},{"level":2,"title":"内存不足","slug":"内存不足","link":"#内存不足","children":[]},{"level":2,"title":"热加载","slug":"热加载","link":"#热加载","children":[]},{"level":2,"title":"终端加载环境变量","slug":"终端加载环境变量","link":"#终端加载环境变量","children":[]},{"level":2,"title":"添加外部jar作为依赖","slug":"添加外部jar作为依赖","link":"#添加外部jar作为依赖","children":[]},{"level":2,"title":"文件找不到——依赖冲突","slug":"文件找不到——依赖冲突","link":"#文件找不到——依赖冲突","children":[]},{"level":2,"title":"自动import","slug":"自动import","link":"#自动import","children":[]},{"level":2,"title":"文件乱码","slug":"文件乱码","link":"#文件乱码","children":[]},{"level":2,"title":"autowired 提示变量未赋值","slug":"autowired-提示变量未赋值","link":"#autowired-提示变量未赋值","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.67,"words":801},"filePathRelative":"java/Resolving-Common-Problems-in-IntelliJ-IDEA.md","localizedDate":"2022年11月25日","excerpt":"

IDEA常见问题与解决方案

\\n

启动参数过长

\\n

Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun.
\\n解决方案:

\\n
    \\n
  1. 编辑 .idea/workspace.xml
  2. \\n
  3. 找到 PropertiesComponent
  4. \\n
  5. 添加:
  6. \\n
\\n

或者这样:
\\n\\"image.png\\"

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-86a15cb6","path":"/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html","title":"IDEA常见问题与解决方案","lang":"zh-CN","frontmatter":{"date":"2022-11-25T00:00:00.000Z","tag":["Java","Daily"],"description":"IDEA常见问题与解决方案 启动参数过长 Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun. 解决方案: 编辑 .idea/workspace.xml 找到 PropertiesComponent 添加: 或者这样:","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"IDEA常见问题与解决方案"}],["meta",{"property":"og:description","content":"IDEA常见问题与解决方案 启动参数过长 Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun. 解决方案: 编辑 .idea/workspace.xml 找到 PropertiesComponent 添加: 或者这样:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-11-25T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"IDEA常见问题与解决方案\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-11-25T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"启动参数过长","slug":"启动参数过长","link":"#启动参数过长","children":[]},{"level":2,"title":"设置JDK版本","slug":"设置jdk版本","link":"#设置jdk版本","children":[]},{"level":2,"title":"lombok 编译报错","slug":"lombok-编译报错","link":"#lombok-编译报错","children":[]},{"level":2,"title":"设置启动参数","slug":"设置启动参数","link":"#设置启动参数","children":[]},{"level":2,"title":"栈溢出","slug":"栈溢出","link":"#栈溢出","children":[]},{"level":2,"title":"内存不足","slug":"内存不足","link":"#内存不足","children":[]},{"level":2,"title":"热加载","slug":"热加载","link":"#热加载","children":[]},{"level":2,"title":"终端加载环境变量","slug":"终端加载环境变量","link":"#终端加载环境变量","children":[]},{"level":2,"title":"添加外部jar作为依赖","slug":"添加外部jar作为依赖","link":"#添加外部jar作为依赖","children":[]},{"level":2,"title":"文件找不到——依赖冲突","slug":"文件找不到——依赖冲突","link":"#文件找不到——依赖冲突","children":[]},{"level":2,"title":"自动import","slug":"自动import","link":"#自动import","children":[]},{"level":2,"title":"文件乱码","slug":"文件乱码","link":"#文件乱码","children":[]},{"level":2,"title":"autowired 提示变量未赋值","slug":"autowired-提示变量未赋值","link":"#autowired-提示变量未赋值","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.67,"words":801},"filePathRelative":"java/Resolving-Common-Problems-in-IntelliJ-IDEA.md","localizedDate":"2022年11月25日","excerpt":"

IDEA常见问题与解决方案

\\n

启动参数过长

\\n

Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun.
\\n解决方案:

\\n
    \\n
  1. 编辑 .idea/workspace.xml
  2. \\n
  3. 找到 PropertiesComponent
  4. \\n
  5. 添加:
  6. \\n
\\n

或者这样:
\\n\\"image.png\\"

","autoDesc":true}');export{e as data}; diff --git a/assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-052f4669.js b/assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-ce1eab94.js similarity index 98% rename from assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-052f4669.js rename to assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-ce1eab94.js index 4dc1fdd5..dd825a1b 100644 --- a/assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-052f4669.js +++ b/assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-ce1eab94.js @@ -1,4 +1,4 @@ -import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as s,c,a as e,b as a,d as n,f as o}from"./app-b649ee34.js";const d={},p=e("h1",{id:"idea常见问题与解决方案",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#idea常见问题与解决方案","aria-hidden":"true"},"#"),a(" IDEA常见问题与解决方案")],-1),h=e("h2",{id:"启动参数过长",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#启动参数过长","aria-hidden":"true"},"#"),a(" 启动参数过长")],-1),g=e("p",null,[a("Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun."),e("br"),a(" 解决方案:")],-1),u=e("li",null,"编辑 .idea/workspace.xml",-1),m=e("li",null,[a("找到 "),e("code",null,"PropertiesComponent")],-1),b=o(`

或者这样:
image.png

"dynamic.classpath": "true",
+import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as s,c,a as e,b as a,d as n,f as o}from"./app-a9d55428.js";const d={},p=e("h1",{id:"idea常见问题与解决方案",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#idea常见问题与解决方案","aria-hidden":"true"},"#"),a(" IDEA常见问题与解决方案")],-1),h=e("h2",{id:"启动参数过长",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#启动参数过长","aria-hidden":"true"},"#"),a(" 启动参数过长")],-1),g=e("p",null,[a("Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun."),e("br"),a(" 解决方案:")],-1),u=e("li",null,"编辑 .idea/workspace.xml",-1),m=e("li",null,[a("找到 "),e("code",null,"PropertiesComponent")],-1),b=o(`

或者这样:
image.png

"dynamic.classpath": "true",
 

设置JDK版本

相关报错:

`,4),f={href:"https://blog.csdn.net/qq_32452623/article/details/106141126",target:"_blank",rel:"noopener noreferrer"},_=e("li",null,"Cannot resolve jdk.tools:jdk.tools:1.7",-1),v=o('

解决方案如下。

1.先确保已安装 jdk。

2.修改运行设置
image.png
image.png
3.修改外部依赖设置
image.png
image.png

lombok 编译报错

',4),k={href:"https://blog.csdn.net/weixin_42440768/article/details/107999786",target:"_blank",rel:"noopener noreferrer"},y=e("br",null,null,-1),x={href:"https://stackoverflow.com/questions/66801256/java-lang-illegalaccesserror-class-lombok-javac-apt-lombokprocessor-cannot-acce",target:"_blank",rel:"noopener noreferrer"},q=o(`

解决方案:找到相应的 pom.xml,更新依赖版本(如果没有,则添加依赖)

<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
diff --git a/assets/Resolving-Common-Problems-in-Maven.md.html-baa4321e.js b/assets/Resolving-Common-Problems-in-Maven.md.html-9fd731f4.js
similarity index 99%
rename from assets/Resolving-Common-Problems-in-Maven.md.html-baa4321e.js
rename to assets/Resolving-Common-Problems-in-Maven.md.html-9fd731f4.js
index 11ab3013..ede7d1ea 100644
--- a/assets/Resolving-Common-Problems-in-Maven.md.html-baa4321e.js
+++ b/assets/Resolving-Common-Problems-in-Maven.md.html-9fd731f4.js
@@ -1,4 +1,4 @@
-import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o,c,a as n,b as a,d as e,f as t}from"./app-b649ee34.js";const i={},r=t(`

Maven常见问题与解决方案

运行 class 找不到主类

maven compile
+import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o,c,a as n,b as a,d as e,f as t}from"./app-a9d55428.js";const i={},r=t(`

Maven常见问题与解决方案

运行 class 找不到主类

maven compile
 

得到 class 文件后

cd /my-app/target/com/mycompany/app
 java App
 

报错:

错误: 找不到或无法加载主类 App
原因: java.lang.NoClassDefFoundError: com/mycompany/app/App (wrong name: App)

这是因为主类并非在默认包下,故需要在正确的路径下调用全限定名。

cd /my-app/target
diff --git a/assets/Resolving-Common-Problems-in-Maven.md.html-4d4645f7.js b/assets/Resolving-Common-Problems-in-Maven.md.html-f77903d5.js
similarity index 91%
rename from assets/Resolving-Common-Problems-in-Maven.md.html-4d4645f7.js
rename to assets/Resolving-Common-Problems-in-Maven.md.html-f77903d5.js
index 55d9300a..f875f4b0 100644
--- a/assets/Resolving-Common-Problems-in-Maven.md.html-4d4645f7.js
+++ b/assets/Resolving-Common-Problems-in-Maven.md.html-f77903d5.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-6dc18ec6","path":"/java/Resolving-Common-Problems-in-Maven.md.html","title":"Maven常见问题与解决方案","lang":"zh-CN","frontmatter":{"date":"2022-12-09T00:00:00.000Z","tag":["Java","Daily"],"description":"Maven常见问题与解决方案 运行 class 找不到主类 maven compile 得到 class 文件后 cd /my-app/target/com/mycompany/app java App","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/Resolving-Common-Problems-in-Maven.md.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Maven常见问题与解决方案"}],["meta",{"property":"og:description","content":"Maven常见问题与解决方案 运行 class 找不到主类 maven compile 得到 class 文件后 cd /my-app/target/com/mycompany/app java App"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-12-09T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Maven常见问题与解决方案\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-12-09T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"运行 class 找不到主类","slug":"运行-class-找不到主类","link":"#运行-class-找不到主类","children":[]},{"level":2,"title":"运行 jar 找不到主类","slug":"运行-jar-找不到主类","link":"#运行-jar-找不到主类","children":[]},{"level":2,"title":"编译时找不到主类","slug":"编译时找不到主类","link":"#编译时找不到主类","children":[]},{"level":2,"title":"设置Maven目录","slug":"设置maven目录","link":"#设置maven目录","children":[]},{"level":2,"title":"无法识别 Maven 项目","slug":"无法识别-maven-项目","link":"#无法识别-maven-项目","children":[]},{"level":2,"title":"使用了不想要的镜像源","slug":"使用了不想要的镜像源","link":"#使用了不想要的镜像源","children":[]},{"level":2,"title":"下载 jar 失败","slug":"下载-jar-失败","link":"#下载-jar-失败","children":[]},{"level":2,"title":"私服认证401","slug":"私服认证401","link":"#私服认证401","children":[]},{"level":2,"title":"避免缓存","slug":"避免缓存","link":"#避免缓存","children":[]},{"level":2,"title":"参考资料","slug":"参考资料","link":"#参考资料","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.28,"words":984},"filePathRelative":"java/Resolving-Common-Problems-in-Maven.md.md","localizedDate":"2022年12月9日","excerpt":"

Maven常见问题与解决方案

\\n

运行 class 找不到主类

\\n
maven compile\\n

得到 class 文件后

\\n
cd /my-app/target/com/mycompany/app\\njava App\\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-6dc18ec6","path":"/java/Resolving-Common-Problems-in-Maven.md.html","title":"Maven常见问题与解决方案","lang":"zh-CN","frontmatter":{"date":"2022-12-09T00:00:00.000Z","tag":["Java","Daily"],"description":"Maven常见问题与解决方案 运行 class 找不到主类 maven compile 得到 class 文件后 cd /my-app/target/com/mycompany/app java App","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/Resolving-Common-Problems-in-Maven.md.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Maven常见问题与解决方案"}],["meta",{"property":"og:description","content":"Maven常见问题与解决方案 运行 class 找不到主类 maven compile 得到 class 文件后 cd /my-app/target/com/mycompany/app java App"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-12-09T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Maven常见问题与解决方案\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-12-09T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"运行 class 找不到主类","slug":"运行-class-找不到主类","link":"#运行-class-找不到主类","children":[]},{"level":2,"title":"运行 jar 找不到主类","slug":"运行-jar-找不到主类","link":"#运行-jar-找不到主类","children":[]},{"level":2,"title":"编译时找不到主类","slug":"编译时找不到主类","link":"#编译时找不到主类","children":[]},{"level":2,"title":"设置Maven目录","slug":"设置maven目录","link":"#设置maven目录","children":[]},{"level":2,"title":"无法识别 Maven 项目","slug":"无法识别-maven-项目","link":"#无法识别-maven-项目","children":[]},{"level":2,"title":"使用了不想要的镜像源","slug":"使用了不想要的镜像源","link":"#使用了不想要的镜像源","children":[]},{"level":2,"title":"下载 jar 失败","slug":"下载-jar-失败","link":"#下载-jar-失败","children":[]},{"level":2,"title":"私服认证401","slug":"私服认证401","link":"#私服认证401","children":[]},{"level":2,"title":"避免缓存","slug":"避免缓存","link":"#避免缓存","children":[]},{"level":2,"title":"参考资料","slug":"参考资料","link":"#参考资料","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.28,"words":984},"filePathRelative":"java/Resolving-Common-Problems-in-Maven.md.md","localizedDate":"2022年12月9日","excerpt":"

Maven常见问题与解决方案

\\n

运行 class 找不到主类

\\n
maven compile\\n

得到 class 文件后

\\n
cd /my-app/target/com/mycompany/app\\njava App\\n
","autoDesc":true}');export{e as data}; diff --git a/assets/a-vuepress2-entertaining-video.html-348ce561.js b/assets/a-vuepress2-entertaining-video.html-c410f2a7.js similarity index 81% rename from assets/a-vuepress2-entertaining-video.html-348ce561.js rename to assets/a-vuepress2-entertaining-video.html-c410f2a7.js index b87ea981..893bac16 100644 --- a/assets/a-vuepress2-entertaining-video.html-348ce561.js +++ b/assets/a-vuepress2-entertaining-video.html-c410f2a7.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-30a50b00","path":"/daily/a-vuepress2-entertaining-video.html","title":"VuePress2 娱乐视频","lang":"zh-CN","frontmatter":{"date":"2023-08-01T00:00:00.000Z","tag":["Daily","Frontend","Video"],"description":"VuePress2 娱乐视频 参考《原神,启动》的梗,做的一个娱乐向视频。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/a-vuepress2-entertaining-video.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"VuePress2 娱乐视频"}],["meta",{"property":"og:description","content":"VuePress2 娱乐视频 参考《原神,启动》的梗,做的一个娱乐向视频。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-01T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"VuePress2 娱乐视频\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-01T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.11,"words":34},"filePathRelative":"daily/a-vuepress2-entertaining-video.md","localizedDate":"2023年8月1日","excerpt":"

VuePress2 娱乐视频

\\n

参考《原神,启动》的梗,做的一个娱乐向视频。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-30a50b00","path":"/daily/a-vuepress2-entertaining-video.html","title":"VuePress2 娱乐视频","lang":"zh-CN","frontmatter":{"date":"2023-08-01T00:00:00.000Z","tag":["Daily","Frontend","Video"],"description":"VuePress2 娱乐视频 参考《原神,启动》的梗,做的一个娱乐向视频。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/a-vuepress2-entertaining-video.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"VuePress2 娱乐视频"}],["meta",{"property":"og:description","content":"VuePress2 娱乐视频 参考《原神,启动》的梗,做的一个娱乐向视频。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-01T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"VuePress2 娱乐视频\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-01T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.11,"words":34},"filePathRelative":"daily/a-vuepress2-entertaining-video.md","localizedDate":"2023年8月1日","excerpt":"

VuePress2 娱乐视频

\\n

参考《原神,启动》的梗,做的一个娱乐向视频。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/a-vuepress2-entertaining-video.html-11220e81.js b/assets/a-vuepress2-entertaining-video.html-d7d94634.js similarity index 89% rename from assets/a-vuepress2-entertaining-video.html-11220e81.js rename to assets/a-vuepress2-entertaining-video.html-d7d94634.js index 702ccc1d..996494af 100644 --- a/assets/a-vuepress2-entertaining-video.html-11220e81.js +++ b/assets/a-vuepress2-entertaining-video.html-d7d94634.js @@ -1 +1 @@ -import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o,c as a,d as n,a as e,b as i}from"./app-b649ee34.js";const c={},d=e("h1",{id:"vuepress2-娱乐视频",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#vuepress2-娱乐视频","aria-hidden":"true"},"#"),i(" VuePress2 娱乐视频")],-1),_=e("p",null,"参考《原神,启动》的梗,做的一个娱乐向视频。",-1);function l(p,u){const s=t("BiliBili");return o(),a("div",null,[d,_,n(s,{bvid:"BV1r14y167c2"})])}const f=r(c,[["render",l],["__file","a-vuepress2-entertaining-video.html.vue"]]);export{f as default}; +import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o,c as a,d as n,a as e,b as i}from"./app-a9d55428.js";const c={},d=e("h1",{id:"vuepress2-娱乐视频",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#vuepress2-娱乐视频","aria-hidden":"true"},"#"),i(" VuePress2 娱乐视频")],-1),_=e("p",null,"参考《原神,启动》的梗,做的一个娱乐向视频。",-1);function l(p,u){const s=t("BiliBili");return o(),a("div",null,[d,_,n(s,{bvid:"BV1r14y167c2"})])}const f=r(c,[["render",l],["__file","a-vuepress2-entertaining-video.html.vue"]]);export{f as default}; diff --git a/assets/a-warning-from-navicat.html-159d00a7.js b/assets/a-warning-from-navicat.html-d1243c4d.js similarity index 84% rename from assets/a-warning-from-navicat.html-159d00a7.js rename to assets/a-warning-from-navicat.html-d1243c4d.js index a0a7445f..173785ce 100644 --- a/assets/a-warning-from-navicat.html-159d00a7.js +++ b/assets/a-warning-from-navicat.html-d1243c4d.js @@ -1 +1 @@ -const t=JSON.parse('{"key":"v-0481cc80","path":"/daily/a-warning-from-navicat.html","title":"来自Navicat的侵权警告","lang":"zh-CN","frontmatter":{"date":"2023-07-31T00:00:00.000Z","tag":["Daily","Video"],"description":"来自Navicat的侵权警告 公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。 另外,开发者常用的软件的合法替代品,视频中也有推荐。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/a-warning-from-navicat.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"来自Navicat的侵权警告"}],["meta",{"property":"og:description","content":"来自Navicat的侵权警告 公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。 另外,开发者常用的软件的合法替代品,视频中也有推荐。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-07-31T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"来自Navicat的侵权警告\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-07-31T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.27,"words":81},"filePathRelative":"daily/a-warning-from-navicat.md","localizedDate":"2023年7月31日","excerpt":"

来自Navicat的侵权警告

\\n

公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。

\\n

另外,开发者常用的软件的合法替代品,视频中也有推荐。

\\n","autoDesc":true}');export{t as data}; +const t=JSON.parse('{"key":"v-0481cc80","path":"/daily/a-warning-from-navicat.html","title":"来自Navicat的侵权警告","lang":"zh-CN","frontmatter":{"date":"2023-07-31T00:00:00.000Z","tag":["Daily","Video"],"description":"来自Navicat的侵权警告 公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。 另外,开发者常用的软件的合法替代品,视频中也有推荐。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/a-warning-from-navicat.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"来自Navicat的侵权警告"}],["meta",{"property":"og:description","content":"来自Navicat的侵权警告 公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。 另外,开发者常用的软件的合法替代品,视频中也有推荐。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-07-31T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"来自Navicat的侵权警告\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-07-31T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.27,"words":81},"filePathRelative":"daily/a-warning-from-navicat.md","localizedDate":"2023年7月31日","excerpt":"

来自Navicat的侵权警告

\\n

公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。

\\n

另外,开发者常用的软件的合法替代品,视频中也有推荐。

\\n","autoDesc":true}');export{t as data}; diff --git a/assets/a-warning-from-navicat.html-1f409a81.js b/assets/a-warning-from-navicat.html-dbede3aa.js similarity index 91% rename from assets/a-warning-from-navicat.html-1f409a81.js rename to assets/a-warning-from-navicat.html-dbede3aa.js index eb99ad83..8283eaaa 100644 --- a/assets/a-warning-from-navicat.html-1f409a81.js +++ b/assets/a-warning-from-navicat.html-dbede3aa.js @@ -1 +1 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o,c as i,d as c,a as e,b as r}from"./app-b649ee34.js";const s={},_=e("h1",{id:"来自navicat的侵权警告",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#来自navicat的侵权警告","aria-hidden":"true"},"#"),r(" 来自Navicat的侵权警告")],-1),l=e("p",null,"公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。",-1),d=e("p",null,"另外,开发者常用的软件的合法替代品,视频中也有推荐。",-1);function m(h,f){const a=n("BiliBili");return o(),i("div",null,[_,l,d,c(a,{bvid:"BV1L94y1e7sx"})])}const u=t(s,[["render",m],["__file","a-warning-from-navicat.html.vue"]]);export{u as default}; +import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o,c as i,d as c,a as e,b as r}from"./app-a9d55428.js";const s={},_=e("h1",{id:"来自navicat的侵权警告",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#来自navicat的侵权警告","aria-hidden":"true"},"#"),r(" 来自Navicat的侵权警告")],-1),l=e("p",null,"公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。",-1),d=e("p",null,"另外,开发者常用的软件的合法替代品,视频中也有推荐。",-1);function m(h,f){const a=n("BiliBili");return o(),i("div",null,[_,l,d,c(a,{bvid:"BV1L94y1e7sx"})])}const u=t(s,[["render",m],["__file","a-warning-from-navicat.html.vue"]]);export{u as default}; diff --git a/assets/about-arm-things-you-need-to-know.html-0feee41c.js b/assets/about-arm-things-you-need-to-know.html-a719ca33.js similarity index 92% rename from assets/about-arm-things-you-need-to-know.html-0feee41c.js rename to assets/about-arm-things-you-need-to-know.html-a719ca33.js index 41e73e39..3c1736f7 100644 --- a/assets/about-arm-things-you-need-to-know.html-0feee41c.js +++ b/assets/about-arm-things-you-need-to-know.html-a719ca33.js @@ -1 +1 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as r,c as l,d as a,a as e,b as i}from"./app-b649ee34.js";const s={},c=e("h1",{id:"关于-arm-你需要了解的三件事",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#关于-arm-你需要了解的三件事","aria-hidden":"true"},"#"),i(" 关于 Arm 你需要了解的三件事")],-1),_=e("p",null,"Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。",-1),d=e("p",null,"跟我们有什么关系呢?",-1),m=e("ol",null,[e("li",null,"MacOS 的 M1 芯片是基于 Arm 的"),e("li",null,"云厂商及生态都在积极与 Arm 进行合作"),e("li",null,"Docker 镜像的构建有注意事项")],-1);function u(h,f){const o=n("BiliBili");return r(),l("div",null,[c,_,d,m,a(o,{bvid:"BV1Eu4y1m75X"})])}const x=t(s,[["render",u],["__file","about-arm-things-you-need-to-know.html.vue"]]);export{x as default}; +import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as r,c as l,d as a,a as e,b as i}from"./app-a9d55428.js";const s={},c=e("h1",{id:"关于-arm-你需要了解的三件事",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#关于-arm-你需要了解的三件事","aria-hidden":"true"},"#"),i(" 关于 Arm 你需要了解的三件事")],-1),_=e("p",null,"Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。",-1),d=e("p",null,"跟我们有什么关系呢?",-1),m=e("ol",null,[e("li",null,"MacOS 的 M1 芯片是基于 Arm 的"),e("li",null,"云厂商及生态都在积极与 Arm 进行合作"),e("li",null,"Docker 镜像的构建有注意事项")],-1);function u(h,f){const o=n("BiliBili");return r(),l("div",null,[c,_,d,m,a(o,{bvid:"BV1Eu4y1m75X"})])}const x=t(s,[["render",u],["__file","about-arm-things-you-need-to-know.html.vue"]]);export{x as default}; diff --git a/assets/about-arm-things-you-need-to-know.html-ce8055c2.js b/assets/about-arm-things-you-need-to-know.html-b26fd653.js similarity index 85% rename from assets/about-arm-things-you-need-to-know.html-ce8055c2.js rename to assets/about-arm-things-you-need-to-know.html-b26fd653.js index 1791b975..d15eaf21 100644 --- a/assets/about-arm-things-you-need-to-know.html-ce8055c2.js +++ b/assets/about-arm-things-you-need-to-know.html-b26fd653.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-31eac486","path":"/daily/about-arm-things-you-need-to-know.html","title":"关于 Arm 你需要了解的三件事","lang":"zh-CN","frontmatter":{"date":"2023-08-02T00:00:00.000Z","tag":["Daily","Linux","Docker"],"description":"关于 Arm 你需要了解的三件事 Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。 跟我们有什么关系呢? MacOS 的 M1 芯片是基于 Arm 的 云厂商及生态都在积极与 Arm 进行合作 Docker 镜像的构建有注意事项","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/about-arm-things-you-need-to-know.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"关于 Arm 你需要了解的三件事"}],["meta",{"property":"og:description","content":"关于 Arm 你需要了解的三件事 Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。 跟我们有什么关系呢? MacOS 的 M1 芯片是基于 Arm 的 云厂商及生态都在积极与 Arm 进行合作 Docker 镜像的构建有注意事项"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Linux"}],["meta",{"property":"article:tag","content":"Docker"}],["meta",{"property":"article:published_time","content":"2023-08-02T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"关于 Arm 你需要了解的三件事\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-02T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.31,"words":92},"filePathRelative":"daily/about-arm-things-you-need-to-know.md","localizedDate":"2023年8月2日","excerpt":"

关于 Arm 你需要了解的三件事

\\n

Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。

\\n

跟我们有什么关系呢?

\\n
    \\n
  1. MacOS 的 M1 芯片是基于 Arm 的
  2. \\n
  3. 云厂商及生态都在积极与 Arm 进行合作
  4. \\n
  5. Docker 镜像的构建有注意事项
  6. \\n
\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-31eac486","path":"/daily/about-arm-things-you-need-to-know.html","title":"关于 Arm 你需要了解的三件事","lang":"zh-CN","frontmatter":{"date":"2023-08-02T00:00:00.000Z","tag":["Daily","Linux","Docker"],"description":"关于 Arm 你需要了解的三件事 Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。 跟我们有什么关系呢? MacOS 的 M1 芯片是基于 Arm 的 云厂商及生态都在积极与 Arm 进行合作 Docker 镜像的构建有注意事项","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/about-arm-things-you-need-to-know.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"关于 Arm 你需要了解的三件事"}],["meta",{"property":"og:description","content":"关于 Arm 你需要了解的三件事 Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。 跟我们有什么关系呢? MacOS 的 M1 芯片是基于 Arm 的 云厂商及生态都在积极与 Arm 进行合作 Docker 镜像的构建有注意事项"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Linux"}],["meta",{"property":"article:tag","content":"Docker"}],["meta",{"property":"article:published_time","content":"2023-08-02T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"关于 Arm 你需要了解的三件事\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-02T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.31,"words":92},"filePathRelative":"daily/about-arm-things-you-need-to-know.md","localizedDate":"2023年8月2日","excerpt":"

关于 Arm 你需要了解的三件事

\\n

Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。

\\n

跟我们有什么关系呢?

\\n
    \\n
  1. MacOS 的 M1 芯片是基于 Arm 的
  2. \\n
  3. 云厂商及生态都在积极与 Arm 进行合作
  4. \\n
  5. Docker 镜像的构建有注意事项
  6. \\n
\\n","autoDesc":true}');export{e as data}; diff --git a/assets/about.html-a18bdc03.js b/assets/about.html-1efe40f7.js similarity index 90% rename from assets/about.html-a18bdc03.js rename to assets/about.html-1efe40f7.js index 7836320b..0f66a49c 100644 --- a/assets/about.html-a18bdc03.js +++ b/assets/about.html-1efe40f7.js @@ -1 +1 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{o,c as a,a as e,b as n}from"./app-b649ee34.js";const s={},c=e("h1",{id:"关于",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#关于","aria-hidden":"true"},"#"),n(" 关于")],-1),r=e("p",null,"从前端到后端,似乎成了全栈。",-1),_=e("p",null,"其实个人的定位更倾向于 Software Engineer。",-1),d=e("p",null,"在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。",-1),i=[c,r,_,d];function l(h,u){return o(),a("div",null,i)}const p=t(s,[["render",l],["__file","about.html.vue"]]);export{p as default}; +import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{o,c as a,a as e,b as n}from"./app-a9d55428.js";const s={},c=e("h1",{id:"关于",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#关于","aria-hidden":"true"},"#"),n(" 关于")],-1),r=e("p",null,"从前端到后端,似乎成了全栈。",-1),_=e("p",null,"其实个人的定位更倾向于 Software Engineer。",-1),d=e("p",null,"在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。",-1),i=[c,r,_,d];function l(h,u){return o(),a("div",null,i)}const p=t(s,[["render",l],["__file","about.html.vue"]]);export{p as default}; diff --git a/assets/about.html-177390a1.js b/assets/about.html-b3827bb1.js similarity index 86% rename from assets/about.html-177390a1.js rename to assets/about.html-b3827bb1.js index ba566da7..2ecae315 100644 --- a/assets/about.html-177390a1.js +++ b/assets/about.html-b3827bb1.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-22a39d25","path":"/about.html","title":"关于","lang":"zh-CN","frontmatter":{"icon":"circle-info","cover":"/assets/images/cover3.jpg","article":false,"description":"关于 从前端到后端,似乎成了全栈。 其实个人的定位更倾向于 Software Engineer。 在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。","head":[["meta",{"property":"og:url","content":"https://levy.vip/about.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"关于"}],["meta",{"property":"og:description","content":"关于 从前端到后端,似乎成了全栈。 其实个人的定位更倾向于 Software Engineer。 在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。"}],["meta",{"property":"og:type","content":"website"}],["meta",{"property":"og:image","content":"https://levy.vip/assets/images/cover3.jpg"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:alt","content":"关于"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"WebPage\\",\\"name\\":\\"关于\\",\\"description\\":\\"关于 从前端到后端,似乎成了全栈。 其实个人的定位更倾向于 Software Engineer。 在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。\\"}"]]},"headers":[],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.24,"words":71},"filePathRelative":"about.md","localizedDate":"2023年10月26日","excerpt":"

关于

\\n

从前端到后端,似乎成了全栈。

\\n

其实个人的定位更倾向于 Software Engineer。

\\n

在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-22a39d25","path":"/about.html","title":"关于","lang":"zh-CN","frontmatter":{"icon":"circle-info","cover":"/assets/images/cover3.jpg","article":false,"description":"关于 从前端到后端,似乎成了全栈。 其实个人的定位更倾向于 Software Engineer。 在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。","head":[["meta",{"property":"og:url","content":"https://levy.vip/about.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"关于"}],["meta",{"property":"og:description","content":"关于 从前端到后端,似乎成了全栈。 其实个人的定位更倾向于 Software Engineer。 在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。"}],["meta",{"property":"og:type","content":"website"}],["meta",{"property":"og:image","content":"https://levy.vip/assets/images/cover3.jpg"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:alt","content":"关于"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"WebPage\\",\\"name\\":\\"关于\\",\\"description\\":\\"关于 从前端到后端,似乎成了全栈。 其实个人的定位更倾向于 Software Engineer。 在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。\\"}"]]},"headers":[],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.24,"words":71},"filePathRelative":"about.md","localizedDate":"2023年11月22日","excerpt":"

关于

\\n

从前端到后端,似乎成了全栈。

\\n

其实个人的定位更倾向于 Software Engineer。

\\n

在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/app-a9d55428.js b/assets/app-a9d55428.js new file mode 100644 index 00000000..1787ddfa --- /dev/null +++ b/assets/app-a9d55428.js @@ -0,0 +1,440 @@ +var Ru=Object.defineProperty;var Su=(e,t,l)=>t in e?Ru(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l;var oa=(e,t,l)=>(Su(e,typeof t!="symbol"?t+"":t,l),l);const Du="modulepreload",$u=function(e){return"/"+e},Xi={},y=function(t,l,n){if(!l||l.length===0)return t();const a=document.getElementsByTagName("link");return Promise.all(l.map(i=>{if(i=$u(i),i in Xi)return;Xi[i]=!0;const r=i.endsWith(".css"),o=r?'[rel="stylesheet"]':"";if(!!n)for(let d=a.length-1;d>=0;d--){const p=a[d];if(p.href===i&&(!r||p.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${i}"]${o}`))return;const u=document.createElement("link");if(u.rel=r?"stylesheet":Du,r||(u.as="script",u.crossOrigin=""),u.href=i,document.head.appendChild(u),r)return new Promise((d,p)=>{u.addEventListener("load",d),u.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${i}`)))})})).then(()=>t()).catch(i=>{const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=i,window.dispatchEvent(r),!r.defaultPrevented)throw i})};function ei(e,t){const l=Object.create(null),n=e.split(",");for(let a=0;a!!l[a.toLowerCase()]:a=>!!l[a]}const xe={},cl=[],st=()=>{},Mu=()=>!1,Vu=/^on[^a-z]/,Zl=e=>Vu.test(e),ti=e=>e.startsWith("onUpdate:"),Ie=Object.assign,li=(e,t)=>{const l=e.indexOf(t);l>-1&&e.splice(l,1)},Fu=Object.prototype.hasOwnProperty,oe=(e,t)=>Fu.call(e,t),X=Array.isArray,Dl=e=>Un(e)==="[object Map]",Nu=e=>Un(e)==="[object Set]",te=e=>typeof e=="function",ae=e=>typeof e=="string",ni=e=>typeof e=="symbol",Te=e=>e!==null&&typeof e=="object",As=e=>Te(e)&&te(e.then)&&te(e.catch),ju=Object.prototype.toString,Un=e=>ju.call(e),Bu=e=>Un(e).slice(8,-1),zu=e=>Un(e)==="[object Object]",ai=e=>ae(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,$l=ei(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),Gn=e=>{const t=Object.create(null);return l=>t[l]||(t[l]=e(l))},Hu=/-(\w)/g,tt=Gn(e=>e.replace(Hu,(t,l)=>l?l.toUpperCase():"")),qu=/\B([A-Z])/g,wl=Gn(e=>e.replace(qu,"-$1").toLowerCase()),en=Gn(e=>e.charAt(0).toUpperCase()+e.slice(1)),ca=Gn(e=>e?`on${en(e)}`:""),zl=(e,t)=>!Object.is(e,t),ua=(e,t)=>{for(let l=0;l{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:l})},Uu=e=>{const t=parseFloat(e);return isNaN(t)?e:t},Gu=e=>{const t=ae(e)?Number(e):NaN;return isNaN(t)?e:t};let Zi;const Oa=()=>Zi||(Zi=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function ii(e){if(X(e)){const t={};for(let l=0;l{if(l){const n=l.split(Ku);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}function ri(e){let t="";if(ae(e))t=e;else if(X(e))for(let l=0;l{const t=new Set(e);return t.w=0,t.n=0,t},Os=e=>(e.w&Nt)>0,Cs=e=>(e.n&Nt)>0,ld=({deps:e})=>{if(e.length)for(let t=0;t{const{deps:t}=e;if(t.length){let l=0;for(let n=0;n{(d==="length"||d>=c)&&o.push(u)})}else switch(l!==void 0&&o.push(r.get(l)),t){case"add":X(e)?ai(l)&&o.push(r.get("length")):(o.push(r.get(Jt)),Dl(e)&&o.push(r.get(Ra)));break;case"delete":X(e)||(o.push(r.get(Jt)),Dl(e)&&o.push(r.get(Ra)));break;case"set":Dl(e)&&o.push(r.get(Jt));break}if(o.length===1)o[0]&&Sa(o[0]);else{const c=[];for(const u of o)u&&c.push(...u);Sa(si(c))}}function Sa(e,t){const l=X(e)?e:[...e];for(const n of l)n.computed&&tr(n);for(const n of l)n.computed||tr(n)}function tr(e,t){(e!==at||e.allowRecurse)&&(e.scheduler?e.scheduler():e.run())}function ad(e,t){var l;return(l=Dn.get(e))==null?void 0:l.get(t)}const id=ei("__proto__,__v_isRef,__isVue"),Ds=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(ni)),rd=ci(),sd=ci(!1,!0),od=ci(!0),lr=cd();function cd(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...l){const n=re(this);for(let i=0,r=this.length;i{e[t]=function(...l){El();const n=re(this)[t].apply(this,l);return xl(),n}}),e}function ud(e){const t=re(this);return qe(t,"has",e),t.hasOwnProperty(e)}function ci(e=!1,t=!1){return function(n,a,i){if(a==="__v_isReactive")return!e;if(a==="__v_isReadonly")return e;if(a==="__v_isShallow")return t;if(a==="__v_raw"&&i===(e?t?Td:Ns:t?Fs:Vs).get(n))return n;const r=X(n);if(!e){if(r&&oe(lr,a))return Reflect.get(lr,a,i);if(a==="hasOwnProperty")return ud}const o=Reflect.get(n,a,i);return(ni(a)?Ds.has(a):id(a))||(e||qe(n,"get",a),t)?o:Oe(o)?r&&ai(a)?o:o.value:Te(o)?e?Yt(o):tn(o):o}}const dd=$s(),pd=$s(!0);function $s(e=!1){return function(l,n,a,i){let r=l[n];if(vl(r)&&Oe(r)&&!Oe(a))return!1;if(!e&&(!$n(a)&&!vl(a)&&(r=re(r),a=re(a)),!X(l)&&Oe(r)&&!Oe(a)))return r.value=a,!0;const o=X(l)&&ai(n)?Number(n)e,Wn=e=>Reflect.getPrototypeOf(e);function yn(e,t,l=!1,n=!1){e=e.__v_raw;const a=re(e),i=re(t);l||(t!==i&&qe(a,"get",t),qe(a,"get",i));const{has:r}=Wn(a),o=n?ui:l?hi:Hl;if(r.call(a,t))return o(e.get(t));if(r.call(a,i))return o(e.get(i));e!==a&&e.get(t)}function bn(e,t=!1){const l=this.__v_raw,n=re(l),a=re(e);return t||(e!==a&&qe(n,"has",e),qe(n,"has",a)),e===a?l.has(e):l.has(e)||l.has(a)}function _n(e,t=!1){return e=e.__v_raw,!t&&qe(re(e),"iterate",Jt),Reflect.get(e,"size",e)}function nr(e){e=re(e);const t=re(this);return Wn(t).has.call(t,e)||(t.add(e),Lt(t,"add",e,e)),this}function ar(e,t){t=re(t);const l=re(this),{has:n,get:a}=Wn(l);let i=n.call(l,e);i||(e=re(e),i=n.call(l,e));const r=a.call(l,e);return l.set(e,t),i?zl(t,r)&&Lt(l,"set",e,t):Lt(l,"add",e,t),this}function ir(e){const t=re(this),{has:l,get:n}=Wn(t);let a=l.call(t,e);a||(e=re(e),a=l.call(t,e)),n&&n.call(t,e);const i=t.delete(e);return a&&Lt(t,"delete",e,void 0),i}function rr(){const e=re(this),t=e.size!==0,l=e.clear();return t&&Lt(e,"clear",void 0,void 0),l}function kn(e,t){return function(n,a){const i=this,r=i.__v_raw,o=re(r),c=t?ui:e?hi:Hl;return!e&&qe(o,"iterate",Jt),r.forEach((u,d)=>n.call(a,c(u),c(d),i))}}function wn(e,t,l){return function(...n){const a=this.__v_raw,i=re(a),r=Dl(i),o=e==="entries"||e===Symbol.iterator&&r,c=e==="keys"&&r,u=a[e](...n),d=l?ui:t?hi:Hl;return!t&&qe(i,"iterate",c?Ra:Jt),{next(){const{value:p,done:h}=u.next();return h?{value:p,done:h}:{value:o?[d(p[0]),d(p[1])]:d(p),done:h}},[Symbol.iterator](){return this}}}}function Pt(e){return function(...t){return e==="delete"?!1:this}}function yd(){const e={get(i){return yn(this,i)},get size(){return _n(this)},has:bn,add:nr,set:ar,delete:ir,clear:rr,forEach:kn(!1,!1)},t={get(i){return yn(this,i,!1,!0)},get size(){return _n(this)},has:bn,add:nr,set:ar,delete:ir,clear:rr,forEach:kn(!1,!0)},l={get(i){return yn(this,i,!0)},get size(){return _n(this,!0)},has(i){return bn.call(this,i,!0)},add:Pt("add"),set:Pt("set"),delete:Pt("delete"),clear:Pt("clear"),forEach:kn(!0,!1)},n={get(i){return yn(this,i,!0,!0)},get size(){return _n(this,!0)},has(i){return bn.call(this,i,!0)},add:Pt("add"),set:Pt("set"),delete:Pt("delete"),clear:Pt("clear"),forEach:kn(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(i=>{e[i]=wn(i,!1,!1),l[i]=wn(i,!0,!1),t[i]=wn(i,!1,!0),n[i]=wn(i,!0,!0)}),[e,l,t,n]}const[bd,_d,kd,wd]=yd();function di(e,t){const l=t?e?wd:kd:e?_d:bd;return(n,a,i)=>a==="__v_isReactive"?!e:a==="__v_isReadonly"?e:a==="__v_raw"?n:Reflect.get(oe(l,a)&&a in n?l:n,a,i)}const Ed={get:di(!1,!1)},xd={get:di(!1,!0)},Ld={get:di(!0,!1)},Vs=new WeakMap,Fs=new WeakMap,Ns=new WeakMap,Td=new WeakMap;function Ad(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function Id(e){return e.__v_skip||!Object.isExtensible(e)?0:Ad(Bu(e))}function tn(e){return vl(e)?e:pi(e,!1,Ms,Ed,Vs)}function js(e){return pi(e,!1,md,xd,Fs)}function Yt(e){return pi(e,!0,gd,Ld,Ns)}function pi(e,t,l,n,a){if(!Te(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const i=a.get(e);if(i)return i;const r=Id(e);if(r===0)return e;const o=new Proxy(e,r===2?n:l);return a.set(e,o),o}function ul(e){return vl(e)?ul(e.__v_raw):!!(e&&e.__v_isReactive)}function vl(e){return!!(e&&e.__v_isReadonly)}function $n(e){return!!(e&&e.__v_isShallow)}function Bs(e){return ul(e)||vl(e)}function re(e){const t=e&&e.__v_raw;return t?re(t):e}function zs(e){return Sn(e,"__v_skip",!0),e}const Hl=e=>Te(e)?tn(e):e,hi=e=>Te(e)?Yt(e):e;function vi(e){Mt&&at&&(e=re(e),Ss(e.dep||(e.dep=si())))}function fi(e,t){e=re(e);const l=e.dep;l&&Sa(l)}function Oe(e){return!!(e&&e.__v_isRef===!0)}function W(e){return Hs(e,!1)}function Ue(e){return Hs(e,!0)}function Hs(e,t){return Oe(e)?e:new Pd(e,t)}class Pd{constructor(t,l){this.__v_isShallow=l,this.dep=void 0,this.__v_isRef=!0,this._rawValue=l?t:re(t),this._value=l?t:Hl(t)}get value(){return vi(this),this._value}set value(t){const l=this.__v_isShallow||$n(t)||vl(t);t=l?t:re(t),zl(t,this._rawValue)&&(this._rawValue=t,this._value=l?t:Hl(t),fi(this))}}function it(e){return Oe(e)?e.value:e}const Od={get:(e,t,l)=>it(Reflect.get(e,t,l)),set:(e,t,l,n)=>{const a=e[t];return Oe(a)&&!Oe(l)?(a.value=l,!0):Reflect.set(e,t,l,n)}};function qs(e){return ul(e)?e:new Proxy(e,Od)}class Cd{constructor(t){this.dep=void 0,this.__v_isRef=!0;const{get:l,set:n}=t(()=>vi(this),()=>fi(this));this._get=l,this._set=n}get value(){return this._get()}set value(t){this._set(t)}}function Rd(e){return new Cd(e)}function Sd(e){const t=X(e)?new Array(e.length):{};for(const l in e)t[l]=Us(e,l);return t}class Dd{constructor(t,l,n){this._object=t,this._key=l,this._defaultValue=n,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return ad(re(this._object),this._key)}}class $d{constructor(t){this._getter=t,this.__v_isRef=!0,this.__v_isReadonly=!0}get value(){return this._getter()}}function Ll(e,t,l){return Oe(e)?e:te(e)?new $d(e):Te(e)&&arguments.length>1?Us(e,t,l):W(e)}function Us(e,t,l){const n=e[t];return Oe(n)?n:new Dd(e,t,l)}class Md{constructor(t,l,n,a){this._setter=l,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this._dirty=!0,this.effect=new oi(t,()=>{this._dirty||(this._dirty=!0,fi(this))}),this.effect.computed=this,this.effect.active=this._cacheable=!a,this.__v_isReadonly=n}get value(){const t=re(this);return vi(t),(t._dirty||!t._cacheable)&&(t._dirty=!1,t._value=t.effect.run()),t._value}set value(t){this._setter(t)}}function Vd(e,t,l=!1){let n,a;const i=te(e);return i?(n=e,a=st):(n=e.get,a=e.set),new Md(n,a,i||!a,l)}function Vt(e,t,l,n){let a;try{a=n?e(...n):e()}catch(i){ln(i,t,l)}return a}function Ze(e,t,l,n){if(te(e)){const i=Vt(e,t,l,n);return i&&As(i)&&i.catch(r=>{ln(r,t,l)}),i}const a=[];for(let i=0;i>>1;Ul($e[n])ft&&$e.splice(t,1)}function Bd(e){X(e)?dl.push(...e):(!xt||!xt.includes(e,e.allowRecurse?Gt+1:Gt))&&dl.push(e),Ws()}function sr(e,t=ql?ft+1:0){for(;t<$e.length;t++){const l=$e[t];l&&l.pre&&($e.splice(t,1),t--,l())}}function Mn(e){if(dl.length){const t=[...new Set(dl)];if(dl.length=0,xt){xt.push(...t);return}for(xt=t,xt.sort((l,n)=>Ul(l)-Ul(n)),Gt=0;Gte.id==null?1/0:e.id,zd=(e,t)=>{const l=Ul(e)-Ul(t);if(l===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return l};function Ks(e){Da=!1,ql=!0,$e.sort(zd);const t=st;try{for(ft=0;ft<$e.length;ft++){const l=$e[ft];l&&l.active!==!1&&Vt(l,null,14)}}finally{ft=0,$e.length=0,Mn(),ql=!1,gi=null,($e.length||dl.length)&&Ks()}}function Hd(e,t,...l){if(e.isUnmounted)return;const n=e.vnode.props||xe;let a=l;const i=t.startsWith("update:"),r=i&&t.slice(7);if(r&&r in n){const d=`${r==="modelValue"?"model":r}Modifiers`,{number:p,trim:h}=n[d]||xe;h&&(a=l.map(v=>ae(v)?v.trim():v)),p&&(a=l.map(Uu))}let o,c=n[o=ca(t)]||n[o=ca(tt(t))];!c&&i&&(c=n[o=ca(wl(t))]),c&&Ze(c,e,6,a);const u=n[o+"Once"];if(u){if(!e.emitted)e.emitted={};else if(e.emitted[o])return;e.emitted[o]=!0,Ze(u,e,6,a)}}function Js(e,t,l=!1){const n=t.emitsCache,a=n.get(e);if(a!==void 0)return a;const i=e.emits;let r={},o=!1;if(!te(e)){const c=u=>{const d=Js(u,t,!0);d&&(o=!0,Ie(r,d))};!l&&t.mixins.length&&t.mixins.forEach(c),e.extends&&c(e.extends),e.mixins&&e.mixins.forEach(c)}return!i&&!o?(Te(e)&&n.set(e,null),null):(X(i)?i.forEach(c=>r[c]=null):Ie(r,i),Te(e)&&n.set(e,r),r)}function Jn(e,t){return!e||!Zl(t)?!1:(t=t.slice(2).replace(/Once$/,""),oe(e,t[0].toLowerCase()+t.slice(1))||oe(e,wl(t))||oe(e,t))}let Xe=null,Qs=null;function Vn(e){const t=Xe;return Xe=e,Qs=e&&e.type.__scopeId||null,t}function qd(e,t=Xe,l){if(!t||e._n)return e;const n=(...a)=>{n._d&&br(-1);const i=Vn(t);let r;try{r=e(...a)}finally{Vn(i),n._d&&br(1)}return r};return n._n=!0,n._c=!0,n._d=!0,n}function da(e){const{type:t,vnode:l,proxy:n,withProxy:a,props:i,propsOptions:[r],slots:o,attrs:c,emit:u,render:d,renderCache:p,data:h,setupState:v,ctx:b,inheritAttrs:w}=e;let x,g;const _=Vn(e);try{if(l.shapeFlag&4){const C=a||n;x=nt(d.call(C,C,p,i,v,h,b)),g=c}else{const C=t;x=nt(C.length>1?C(i,{attrs:c,slots:o,emit:u}):C(i,null)),g=t.props?c:Ud(c)}}catch(C){Nl.length=0,ln(C,e,1),x=Ae(et)}let P=x;if(g&&w!==!1){const C=Object.keys(g),{shapeFlag:B}=P;C.length&&B&7&&(r&&C.some(ti)&&(g=Gd(g,r)),P=jt(P,g))}return l.dirs&&(P=jt(P),P.dirs=P.dirs?P.dirs.concat(l.dirs):l.dirs),l.transition&&(P.transition=l.transition),x=P,Vn(_),x}const Ud=e=>{let t;for(const l in e)(l==="class"||l==="style"||Zl(l))&&((t||(t={}))[l]=e[l]);return t},Gd=(e,t)=>{const l={};for(const n in e)(!ti(n)||!(n.slice(9)in t))&&(l[n]=e[n]);return l};function Wd(e,t,l){const{props:n,children:a,component:i}=e,{props:r,children:o,patchFlag:c}=t,u=i.emitsOptions;if(t.dirs||t.transition)return!0;if(l&&c>=0){if(c&1024)return!0;if(c&16)return n?or(n,r,u):!!r;if(c&8){const d=t.dynamicProps;for(let p=0;pe.__isSuspense;function Ys(e,t){t&&t.pendingBranch?X(e)?t.effects.push(...e):t.effects.push(e):Bd(e)}function Xs(e,t){return mi(e,null,t)}const En={};function ce(e,t,l){return mi(e,t,l)}function mi(e,t,{immediate:l,deep:n,flush:a,onTrack:i,onTrigger:r}=xe){var o;const c=Ps()===((o=Ce)==null?void 0:o.scope)?Ce:null;let u,d=!1,p=!1;if(Oe(e)?(u=()=>e.value,d=$n(e)):ul(e)?(u=()=>e,n=!0):X(e)?(p=!0,d=e.some(C=>ul(C)||$n(C)),u=()=>e.map(C=>{if(Oe(C))return C.value;if(ul(C))return sl(C);if(te(C))return Vt(C,c,2)})):te(e)?t?u=()=>Vt(e,c,2):u=()=>{if(!(c&&c.isUnmounted))return h&&h(),Ze(e,c,3,[v])}:u=st,t&&n){const C=u;u=()=>sl(C())}let h,v=C=>{h=_.onStop=()=>{Vt(C,c,4)}},b;if(ml)if(v=st,t?l&&Ze(t,c,3,[u(),p?[]:void 0,v]):u(),a==="sync"){const C=qp();b=C.__watcherHandles||(C.__watcherHandles=[])}else return st;let w=p?new Array(e.length).fill(En):En;const x=()=>{if(_.active)if(t){const C=_.run();(n||d||(p?C.some((B,R)=>zl(B,w[R])):zl(C,w)))&&(h&&h(),Ze(t,c,3,[C,w===En?void 0:p&&w[0]===En?[]:w,v]),w=C)}else _.run()};x.allowRecurse=!!t;let g;a==="sync"?g=x:a==="post"?g=()=>Be(x,c&&c.suspense):(x.pre=!0,c&&(x.id=c.uid),g=()=>Kn(x));const _=new oi(u,g);t?l?x():w=_.run():a==="post"?Be(_.run.bind(_),c&&c.suspense):_.run();const P=()=>{_.stop(),c&&c.scope&&li(c.scope.effects,_)};return b&&b.push(P),P}function Qd(e,t,l){const n=this.proxy,a=ae(e)?e.includes(".")?Zs(n,e):()=>n[e]:e.bind(n,n);let i;te(t)?i=t:(i=t.handler,l=t);const r=Ce;gl(this);const o=mi(a,i.bind(n),l);return r?gl(r):Qt(),o}function Zs(e,t){const l=t.split(".");return()=>{let n=e;for(let a=0;a{sl(l,t)});else if(zu(e))for(const l in e)sl(e[l],t);return e}function vt(e,t,l,n){const a=e.dirs,i=t&&t.dirs;for(let r=0;r{e.isMounted=!0}),Yn(()=>{e.isUnmounting=!0}),e}const Qe=[Function,Array],to={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Qe,onEnter:Qe,onAfterEnter:Qe,onEnterCancelled:Qe,onBeforeLeave:Qe,onLeave:Qe,onAfterLeave:Qe,onLeaveCancelled:Qe,onBeforeAppear:Qe,onAppear:Qe,onAfterAppear:Qe,onAppearCancelled:Qe},Yd={name:"BaseTransition",props:to,setup(e,{slots:t}){const l=Xt(),n=eo();let a;return()=>{const i=t.default&&yi(t.default(),!0);if(!i||!i.length)return;let r=i[0];if(i.length>1){for(const w of i)if(w.type!==et){r=w;break}}const o=re(e),{mode:c}=o;if(n.isLeaving)return pa(r);const u=cr(r);if(!u)return pa(r);const d=Gl(u,o,n,l);Wl(u,d);const p=l.subTree,h=p&&cr(p);let v=!1;const{getTransitionKey:b}=u.type;if(b){const w=b();a===void 0?a=w:w!==a&&(a=w,v=!0)}if(h&&h.type!==et&&(!Wt(u,h)||v)){const w=Gl(h,o,n,l);if(Wl(h,w),c==="out-in")return n.isLeaving=!0,w.afterLeave=()=>{n.isLeaving=!1,l.update.active!==!1&&l.update()},pa(r);c==="in-out"&&u.type!==et&&(w.delayLeave=(x,g,_)=>{const P=lo(n,h);P[String(h.key)]=h,x._leaveCb=()=>{g(),x._leaveCb=void 0,delete d.delayedLeave},d.delayedLeave=_})}return r}}},Xd=Yd;function lo(e,t){const{leavingVNodes:l}=e;let n=l.get(t.type);return n||(n=Object.create(null),l.set(t.type,n)),n}function Gl(e,t,l,n){const{appear:a,mode:i,persisted:r=!1,onBeforeEnter:o,onEnter:c,onAfterEnter:u,onEnterCancelled:d,onBeforeLeave:p,onLeave:h,onAfterLeave:v,onLeaveCancelled:b,onBeforeAppear:w,onAppear:x,onAfterAppear:g,onAppearCancelled:_}=t,P=String(e.key),C=lo(l,e),B=(T,q)=>{T&&Ze(T,n,9,q)},R=(T,q)=>{const Y=q[1];B(T,q),X(T)?T.every(ne=>ne.length<=1)&&Y():T.length<=1&&Y()},V={mode:i,persisted:r,beforeEnter(T){let q=o;if(!l.isMounted)if(a)q=w||o;else return;T._leaveCb&&T._leaveCb(!0);const Y=C[P];Y&&Wt(e,Y)&&Y.el._leaveCb&&Y.el._leaveCb(),B(q,[T])},enter(T){let q=c,Y=u,ne=d;if(!l.isMounted)if(a)q=x||c,Y=g||u,ne=_||d;else return;let H=!1;const ee=T._enterCb=G=>{H||(H=!0,G?B(ne,[T]):B(Y,[T]),V.delayedLeave&&V.delayedLeave(),T._enterCb=void 0)};q?R(q,[T,ee]):ee()},leave(T,q){const Y=String(e.key);if(T._enterCb&&T._enterCb(!0),l.isUnmounting)return q();B(p,[T]);let ne=!1;const H=T._leaveCb=ee=>{ne||(ne=!0,q(),ee?B(b,[T]):B(v,[T]),T._leaveCb=void 0,C[Y]===e&&delete C[Y])};C[Y]=e,h?R(h,[T,H]):H()},clone(T){return Gl(T,t,l,n)}};return V}function pa(e){if(nn(e))return e=jt(e),e.children=null,e}function cr(e){return nn(e)?e.children?e.children[0]:void 0:e}function Wl(e,t){e.shapeFlag&6&&e.component?Wl(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function yi(e,t=!1,l){let n=[],a=0;for(let i=0;i1)for(let i=0;iIe({name:e.name},t,{setup:e}))():e}const Ml=e=>!!e.type.__asyncLoader;function O(e){te(e)&&(e={loader:e});const{loader:t,loadingComponent:l,errorComponent:n,delay:a=200,timeout:i,suspensible:r=!0,onError:o}=e;let c=null,u,d=0;const p=()=>(d++,c=null,h()),h=()=>{let v;return c||(v=c=t().catch(b=>{if(b=b instanceof Error?b:new Error(String(b)),o)return new Promise((w,x)=>{o(b,()=>w(p()),()=>x(b),d+1)});throw b}).then(b=>v!==c&&c?c:(b&&(b.__esModule||b[Symbol.toStringTag]==="Module")&&(b=b.default),u=b,b)))};return M({name:"AsyncComponentWrapper",__asyncLoader:h,get __asyncResolved(){return u},setup(){const v=Ce;if(u)return()=>ha(u,v);const b=_=>{c=null,ln(_,v,13,!n)};if(r&&v.suspense||ml)return h().then(_=>()=>ha(_,v)).catch(_=>(b(_),()=>n?Ae(n,{error:_}):null));const w=W(!1),x=W(),g=W(!!a);return a&&setTimeout(()=>{g.value=!1},a),i!=null&&setTimeout(()=>{if(!w.value&&!x.value){const _=new Error(`Async component timed out after ${i}ms.`);b(_),x.value=_}},i),h().then(()=>{w.value=!0,v.parent&&nn(v.parent.vnode)&&Kn(v.parent.update)}).catch(_=>{b(_),x.value=_}),()=>{if(w.value&&u)return ha(u,v);if(x.value&&n)return Ae(n,{error:x.value});if(l&&!g.value)return Ae(l)}}})}function ha(e,t){const{ref:l,props:n,children:a,ce:i}=t.vnode,r=Ae(e,n,a);return r.ref=l,r.ce=i,delete t.vnode.ce,r}const nn=e=>e.type.__isKeepAlive;function Zd(e,t){no(e,"a",t)}function ep(e,t){no(e,"da",t)}function no(e,t,l=Ce){const n=e.__wdc||(e.__wdc=()=>{let a=l;for(;a;){if(a.isDeactivated)return;a=a.parent}return e()});if(Qn(t,n,l),l){let a=l.parent;for(;a&&a.parent;)nn(a.parent.vnode)&&tp(n,t,l,a),a=a.parent}}function tp(e,t,l,n){const a=Qn(t,e,n,!0);an(()=>{li(n[t],a)},l)}function Qn(e,t,l=Ce,n=!1){if(l){const a=l[e]||(l[e]=[]),i=t.__weh||(t.__weh=(...r)=>{if(l.isUnmounted)return;El(),gl(l);const o=Ze(t,l,e,r);return Qt(),xl(),o});return n?a.unshift(i):a.push(i),i}}const At=e=>(t,l=Ce)=>(!ml||e==="sp")&&Qn(e,(...n)=>t(...n),l),lp=At("bm"),ke=At("m"),np=At("bu"),ao=At("u"),Yn=At("bum"),an=At("um"),ap=At("sp"),ip=At("rtg"),rp=At("rtc");function sp(e,t=Ce){Qn("ec",e,t)}const io="components";function Je(e,t){return cp(io,e,!0,t)||e}const op=Symbol.for("v-ndc");function cp(e,t,l=!0,n=!1){const a=Xe||Ce;if(a){const i=a.type;if(e===io){const o=Bp(i,!1);if(o&&(o===t||o===tt(t)||o===en(tt(t))))return i}const r=ur(a[e]||i[e],t)||ur(a.appContext[e],t);return!r&&n?i:r}}function ur(e,t){return e&&(e[t]||e[tt(t)]||e[en(tt(t))])}const $a=e=>e?bo(e)?Ei(e)||e.proxy:$a(e.parent):null,Vl=Ie(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>$a(e.parent),$root:e=>$a(e.root),$emit:e=>e.emit,$options:e=>bi(e),$forceUpdate:e=>e.f||(e.f=()=>Kn(e.update)),$nextTick:e=>e.n||(e.n=Tl.bind(e.proxy)),$watch:e=>Qd.bind(e)}),va=(e,t)=>e!==xe&&!e.__isScriptSetup&&oe(e,t),up={get({_:e},t){const{ctx:l,setupState:n,data:a,props:i,accessCache:r,type:o,appContext:c}=e;let u;if(t[0]!=="$"){const v=r[t];if(v!==void 0)switch(v){case 1:return n[t];case 2:return a[t];case 4:return l[t];case 3:return i[t]}else{if(va(n,t))return r[t]=1,n[t];if(a!==xe&&oe(a,t))return r[t]=2,a[t];if((u=e.propsOptions[0])&&oe(u,t))return r[t]=3,i[t];if(l!==xe&&oe(l,t))return r[t]=4,l[t];Ma&&(r[t]=0)}}const d=Vl[t];let p,h;if(d)return t==="$attrs"&&qe(e,"get",t),d(e);if((p=o.__cssModules)&&(p=p[t]))return p;if(l!==xe&&oe(l,t))return r[t]=4,l[t];if(h=c.config.globalProperties,oe(h,t))return h[t]},set({_:e},t,l){const{data:n,setupState:a,ctx:i}=e;return va(a,t)?(a[t]=l,!0):n!==xe&&oe(n,t)?(n[t]=l,!0):oe(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(i[t]=l,!0)},has({_:{data:e,setupState:t,accessCache:l,ctx:n,appContext:a,propsOptions:i}},r){let o;return!!l[r]||e!==xe&&oe(e,r)||va(t,r)||(o=i[0])&&oe(o,r)||oe(n,r)||oe(Vl,r)||oe(a.config.globalProperties,r)},defineProperty(e,t,l){return l.get!=null?e._.accessCache[t]=0:oe(l,"value")&&this.set(e,t,l.value,null),Reflect.defineProperty(e,t,l)}};function dr(e){return X(e)?e.reduce((t,l)=>(t[l]=null,t),{}):e}let Ma=!0;function dp(e){const t=bi(e),l=e.proxy,n=e.ctx;Ma=!1,t.beforeCreate&&pr(t.beforeCreate,e,"bc");const{data:a,computed:i,methods:r,watch:o,provide:c,inject:u,created:d,beforeMount:p,mounted:h,beforeUpdate:v,updated:b,activated:w,deactivated:x,beforeDestroy:g,beforeUnmount:_,destroyed:P,unmounted:C,render:B,renderTracked:R,renderTriggered:V,errorCaptured:T,serverPrefetch:q,expose:Y,inheritAttrs:ne,components:H,directives:ee,filters:G}=t;if(u&&pp(u,n,null),r)for(const _e in r){const ve=r[_e];te(ve)&&(n[_e]=ve.bind(l))}if(a){const _e=a.call(l,l);Te(_e)&&(e.data=tn(_e))}if(Ma=!0,i)for(const _e in i){const ve=i[_e],bt=te(ve)?ve.bind(l,l):te(ve.get)?ve.get.bind(l,l):st,It=!te(ve)&&te(ve.set)?ve.set.bind(l):st,pt=E({get:bt,set:It});Object.defineProperty(n,_e,{enumerable:!0,configurable:!0,get:()=>pt.value,set:je=>pt.value=je})}if(o)for(const _e in o)ro(o[_e],n,l,_e);if(c){const _e=te(c)?c.call(l):c;Reflect.ownKeys(_e).forEach(ve=>{ot(ve,_e[ve])})}d&&pr(d,e,"c");function he(_e,ve){X(ve)?ve.forEach(bt=>_e(bt.bind(l))):ve&&_e(ve.bind(l))}if(he(lp,p),he(ke,h),he(np,v),he(ao,b),he(Zd,w),he(ep,x),he(sp,T),he(rp,R),he(ip,V),he(Yn,_),he(an,C),he(ap,q),X(Y))if(Y.length){const _e=e.exposed||(e.exposed={});Y.forEach(ve=>{Object.defineProperty(_e,ve,{get:()=>l[ve],set:bt=>l[ve]=bt})})}else e.exposed||(e.exposed={});B&&e.render===st&&(e.render=B),ne!=null&&(e.inheritAttrs=ne),H&&(e.components=H),ee&&(e.directives=ee)}function pp(e,t,l=st){X(e)&&(e=Va(e));for(const n in e){const a=e[n];let i;Te(a)?"default"in a?i=me(a.from||n,a.default,!0):i=me(a.from||n):i=me(a),Oe(i)?Object.defineProperty(t,n,{enumerable:!0,configurable:!0,get:()=>i.value,set:r=>i.value=r}):t[n]=i}}function pr(e,t,l){Ze(X(e)?e.map(n=>n.bind(t.proxy)):e.bind(t.proxy),t,l)}function ro(e,t,l,n){const a=n.includes(".")?Zs(l,n):()=>l[n];if(ae(e)){const i=t[e];te(i)&&ce(a,i)}else if(te(e))ce(a,e.bind(l));else if(Te(e))if(X(e))e.forEach(i=>ro(i,t,l,n));else{const i=te(e.handler)?e.handler.bind(l):t[e.handler];te(i)&&ce(a,i,e)}}function bi(e){const t=e.type,{mixins:l,extends:n}=t,{mixins:a,optionsCache:i,config:{optionMergeStrategies:r}}=e.appContext,o=i.get(t);let c;return o?c=o:!a.length&&!l&&!n?c=t:(c={},a.length&&a.forEach(u=>Fn(c,u,r,!0)),Fn(c,t,r)),Te(t)&&i.set(t,c),c}function Fn(e,t,l,n=!1){const{mixins:a,extends:i}=t;i&&Fn(e,i,l,!0),a&&a.forEach(r=>Fn(e,r,l,!0));for(const r in t)if(!(n&&r==="expose")){const o=hp[r]||l&&l[r];e[r]=o?o(e[r],t[r]):t[r]}return e}const hp={data:hr,props:vr,emits:vr,methods:Sl,computed:Sl,beforeCreate:Fe,created:Fe,beforeMount:Fe,mounted:Fe,beforeUpdate:Fe,updated:Fe,beforeDestroy:Fe,beforeUnmount:Fe,destroyed:Fe,unmounted:Fe,activated:Fe,deactivated:Fe,errorCaptured:Fe,serverPrefetch:Fe,components:Sl,directives:Sl,watch:fp,provide:hr,inject:vp};function hr(e,t){return t?e?function(){return Ie(te(e)?e.call(this,this):e,te(t)?t.call(this,this):t)}:t:e}function vp(e,t){return Sl(Va(e),Va(t))}function Va(e){if(X(e)){const t={};for(let l=0;l1)return l&&te(t)?t.call(n&&n.proxy):t}}function yp(e,t,l,n=!1){const a={},i={};Sn(i,Xn,1),e.propsDefaults=Object.create(null),oo(e,t,a,i);for(const r in e.propsOptions[0])r in a||(a[r]=void 0);l?e.props=n?a:js(a):e.type.props?e.props=a:e.props=i,e.attrs=i}function bp(e,t,l,n){const{props:a,attrs:i,vnode:{patchFlag:r}}=e,o=re(a),[c]=e.propsOptions;let u=!1;if((n||r>0)&&!(r&16)){if(r&8){const d=e.vnode.dynamicProps;for(let p=0;p{c=!0;const[h,v]=co(p,t,!0);Ie(r,h),v&&o.push(...v)};!l&&t.mixins.length&&t.mixins.forEach(d),e.extends&&d(e.extends),e.mixins&&e.mixins.forEach(d)}if(!i&&!c)return Te(e)&&n.set(e,cl),cl;if(X(i))for(let d=0;d-1,v[1]=w<0||b-1||oe(v,"default"))&&o.push(p)}}}const u=[r,o];return Te(e)&&n.set(e,u),u}function fr(e){return e[0]!=="$"}function gr(e){const t=e&&e.toString().match(/^\s*(function|class) (\w+)/);return t?t[2]:e===null?"null":""}function mr(e,t){return gr(e)===gr(t)}function yr(e,t){return X(t)?t.findIndex(l=>mr(l,e)):te(t)&&mr(t,e)?0:-1}const uo=e=>e[0]==="_"||e==="$stable",_i=e=>X(e)?e.map(nt):[nt(e)],_p=(e,t,l)=>{if(t._n)return t;const n=qd((...a)=>_i(t(...a)),l);return n._c=!1,n},po=(e,t,l)=>{const n=e._ctx;for(const a in e){if(uo(a))continue;const i=e[a];if(te(i))t[a]=_p(a,i,n);else if(i!=null){const r=_i(i);t[a]=()=>r}}},ho=(e,t)=>{const l=_i(t);e.slots.default=()=>l},kp=(e,t)=>{if(e.vnode.shapeFlag&32){const l=t._;l?(e.slots=re(t),Sn(t,"_",l)):po(t,e.slots={})}else e.slots={},t&&ho(e,t);Sn(e.slots,Xn,1)},wp=(e,t,l)=>{const{vnode:n,slots:a}=e;let i=!0,r=xe;if(n.shapeFlag&32){const o=t._;o?l&&o===1?i=!1:(Ie(a,t),!l&&o===1&&delete a._):(i=!t.$stable,po(t,a)),r=t}else t&&(ho(e,t),r={default:1});if(i)for(const o in a)!uo(o)&&!(o in r)&&delete a[o]};function jn(e,t,l,n,a=!1){if(X(e)){e.forEach((h,v)=>jn(h,t&&(X(t)?t[v]:t),l,n,a));return}if(Ml(n)&&!a)return;const i=n.shapeFlag&4?Ei(n.component)||n.component.proxy:n.el,r=a?null:i,{i:o,r:c}=e,u=t&&t.r,d=o.refs===xe?o.refs={}:o.refs,p=o.setupState;if(u!=null&&u!==c&&(ae(u)?(d[u]=null,oe(p,u)&&(p[u]=null)):Oe(u)&&(u.value=null)),te(c))Vt(c,o,12,[r,d]);else{const h=ae(c),v=Oe(c);if(h||v){const b=()=>{if(e.f){const w=h?oe(p,c)?p[c]:d[c]:c.value;a?X(w)&&li(w,i):X(w)?w.includes(i)||w.push(i):h?(d[c]=[i],oe(p,c)&&(p[c]=d[c])):(c.value=[i],e.k&&(d[e.k]=c.value))}else h?(d[c]=r,oe(p,c)&&(p[c]=r)):v&&(c.value=r,e.k&&(d[e.k]=r))};r?(b.id=-1,Be(b,l)):b()}}}let Ot=!1;const xn=e=>/svg/.test(e.namespaceURI)&&e.tagName!=="foreignObject",Ln=e=>e.nodeType===8;function Ep(e){const{mt:t,p:l,o:{patchProp:n,createText:a,nextSibling:i,parentNode:r,remove:o,insert:c,createComment:u}}=e,d=(g,_)=>{if(!_.hasChildNodes()){l(null,g,_),Mn(),_._vnode=g;return}Ot=!1,p(_.firstChild,g,null,null,null),Mn(),_._vnode=g,Ot&&console.error("Hydration completed but contains mismatches.")},p=(g,_,P,C,B,R=!1)=>{const V=Ln(g)&&g.data==="[",T=()=>w(g,_,P,C,B,V),{type:q,ref:Y,shapeFlag:ne,patchFlag:H}=_;let ee=g.nodeType;_.el=g,H===-2&&(R=!1,_.dynamicChildren=null);let G=null;switch(q){case fl:ee!==3?_.children===""?(c(_.el=a(""),r(g),g),G=g):G=T():(g.data!==_.children&&(Ot=!0,g.data=_.children),G=i(g));break;case et:ee!==8||V?G=T():G=i(g);break;case Fl:if(V&&(g=i(g),ee=g.nodeType),ee===1||ee===3){G=g;const Se=!_.children.length;for(let he=0;he<_.staticCount;he++)Se&&(_.children+=G.nodeType===1?G.outerHTML:G.data),he===_.staticCount-1&&(_.anchor=G),G=i(G);return V?i(G):G}else T();break;case Ke:V?G=b(g,_,P,C,B,R):G=T();break;default:if(ne&1)ee!==1||_.type.toLowerCase()!==g.tagName.toLowerCase()?G=T():G=h(g,_,P,C,B,R);else if(ne&6){_.slotScopeIds=B;const Se=r(g);if(t(_,Se,null,P,C,xn(Se),R),G=V?x(g):i(g),G&&Ln(G)&&G.data==="teleport end"&&(G=i(G)),Ml(_)){let he;V?(he=Ae(Ke),he.anchor=G?G.previousSibling:Se.lastChild):he=g.nodeType===3?yo(""):Ae("div"),he.el=g,_.component.subTree=he}}else ne&64?ee!==8?G=T():G=_.type.hydrate(g,_,P,C,B,R,e,v):ne&128&&(G=_.type.hydrate(g,_,P,C,xn(r(g)),B,R,e,p))}return Y!=null&&jn(Y,null,C,_),G},h=(g,_,P,C,B,R)=>{R=R||!!_.dynamicChildren;const{type:V,props:T,patchFlag:q,shapeFlag:Y,dirs:ne}=_,H=V==="input"&&ne||V==="option";if(H||q!==-1){if(ne&&vt(_,null,P,"created"),T)if(H||!R||q&48)for(const G in T)(H&&G.endsWith("value")||Zl(G)&&!$l(G))&&n(g,G,null,T[G],!1,void 0,P);else T.onClick&&n(g,"onClick",null,T.onClick,!1,void 0,P);let ee;if((ee=T&&T.onVnodeBeforeMount)&&Ye(ee,P,_),ne&&vt(_,null,P,"beforeMount"),((ee=T&&T.onVnodeMounted)||ne)&&Ys(()=>{ee&&Ye(ee,P,_),ne&&vt(_,null,P,"mounted")},C),Y&16&&!(T&&(T.innerHTML||T.textContent))){let G=v(g.firstChild,_,g,P,C,B,R);for(;G;){Ot=!0;const Se=G;G=G.nextSibling,o(Se)}}else Y&8&&g.textContent!==_.children&&(Ot=!0,g.textContent=_.children)}return g.nextSibling},v=(g,_,P,C,B,R,V)=>{V=V||!!_.dynamicChildren;const T=_.children,q=T.length;for(let Y=0;Y{const{slotScopeIds:V}=_;V&&(B=B?B.concat(V):V);const T=r(g),q=v(i(g),_,T,P,C,B,R);return q&&Ln(q)&&q.data==="]"?i(_.anchor=q):(Ot=!0,c(_.anchor=u("]"),T,q),q)},w=(g,_,P,C,B,R)=>{if(Ot=!0,_.el=null,R){const q=x(g);for(;;){const Y=i(g);if(Y&&Y!==q)o(Y);else break}}const V=i(g),T=r(g);return o(g),l(null,_,T,V,P,C,xn(T),B),V},x=g=>{let _=0;for(;g;)if(g=i(g),g&&Ln(g)&&(g.data==="["&&_++,g.data==="]")){if(_===0)return i(g);_--}return g};return[d,p]}const Be=Ys;function xp(e){return Lp(e,Ep)}function Lp(e,t){const l=Oa();l.__VUE__=!0;const{insert:n,remove:a,patchProp:i,createElement:r,createText:o,createComment:c,setText:u,setElementText:d,parentNode:p,nextSibling:h,setScopeId:v=st,insertStaticContent:b}=e,w=(f,m,k,L=null,I=null,S=null,j=!1,$=null,F=!!m.dynamicChildren)=>{if(f===m)return;f&&!Wt(f,m)&&(L=A(f),je(f,I,S,!0),f=null),m.patchFlag===-2&&(F=!1,m.dynamicChildren=null);const{type:D,ref:J,shapeFlag:U}=m;switch(D){case fl:x(f,m,k,L);break;case et:g(f,m,k,L);break;case Fl:f==null&&_(m,k,L,j);break;case Ke:H(f,m,k,L,I,S,j,$,F);break;default:U&1?B(f,m,k,L,I,S,j,$,F):U&6?ee(f,m,k,L,I,S,j,$,F):(U&64||U&128)&&D.process(f,m,k,L,I,S,j,$,F,N)}J!=null&&I&&jn(J,f&&f.ref,S,m||f,!m)},x=(f,m,k,L)=>{if(f==null)n(m.el=o(m.children),k,L);else{const I=m.el=f.el;m.children!==f.children&&u(I,m.children)}},g=(f,m,k,L)=>{f==null?n(m.el=c(m.children||""),k,L):m.el=f.el},_=(f,m,k,L)=>{[f.el,f.anchor]=b(f.children,m,k,L,f.el,f.anchor)},P=({el:f,anchor:m},k,L)=>{let I;for(;f&&f!==m;)I=h(f),n(f,k,L),f=I;n(m,k,L)},C=({el:f,anchor:m})=>{let k;for(;f&&f!==m;)k=h(f),a(f),f=k;a(m)},B=(f,m,k,L,I,S,j,$,F)=>{j=j||m.type==="svg",f==null?R(m,k,L,I,S,j,$,F):q(f,m,I,S,j,$,F)},R=(f,m,k,L,I,S,j,$)=>{let F,D;const{type:J,props:U,shapeFlag:Q,transition:Z,dirs:le}=f;if(F=f.el=r(f.type,S,U&&U.is,U),Q&8?d(F,f.children):Q&16&&T(f.children,F,null,L,I,S&&J!=="foreignObject",j,$),le&&vt(f,null,L,"created"),V(F,f,f.scopeId,j,L),U){for(const ye in U)ye!=="value"&&!$l(ye)&&i(F,ye,null,U[ye],S,f.children,L,I,De);"value"in U&&i(F,"value",null,U.value),(D=U.onVnodeBeforeMount)&&Ye(D,L,f)}le&&vt(f,null,L,"beforeMount");const we=(!I||I&&!I.pendingBranch)&&Z&&!Z.persisted;we&&Z.beforeEnter(F),n(F,m,k),((D=U&&U.onVnodeMounted)||we||le)&&Be(()=>{D&&Ye(D,L,f),we&&Z.enter(F),le&&vt(f,null,L,"mounted")},I)},V=(f,m,k,L,I)=>{if(k&&v(f,k),L)for(let S=0;S{for(let D=F;D{const $=m.el=f.el;let{patchFlag:F,dynamicChildren:D,dirs:J}=m;F|=f.patchFlag&16;const U=f.props||xe,Q=m.props||xe;let Z;k&&qt(k,!1),(Z=Q.onVnodeBeforeUpdate)&&Ye(Z,k,m,f),J&&vt(m,f,k,"beforeUpdate"),k&&qt(k,!0);const le=I&&m.type!=="foreignObject";if(D?Y(f.dynamicChildren,D,$,k,L,le,S):j||ve(f,m,$,null,k,L,le,S,!1),F>0){if(F&16)ne($,m,U,Q,k,L,I);else if(F&2&&U.class!==Q.class&&i($,"class",null,Q.class,I),F&4&&i($,"style",U.style,Q.style,I),F&8){const we=m.dynamicProps;for(let ye=0;ye{Z&&Ye(Z,k,m,f),J&&vt(m,f,k,"updated")},L)},Y=(f,m,k,L,I,S,j)=>{for(let $=0;${if(k!==L){if(k!==xe)for(const $ in k)!$l($)&&!($ in L)&&i(f,$,k[$],null,j,m.children,I,S,De);for(const $ in L){if($l($))continue;const F=L[$],D=k[$];F!==D&&$!=="value"&&i(f,$,D,F,j,m.children,I,S,De)}"value"in L&&i(f,"value",k.value,L.value)}},H=(f,m,k,L,I,S,j,$,F)=>{const D=m.el=f?f.el:o(""),J=m.anchor=f?f.anchor:o("");let{patchFlag:U,dynamicChildren:Q,slotScopeIds:Z}=m;Z&&($=$?$.concat(Z):Z),f==null?(n(D,k,L),n(J,k,L),T(m.children,k,J,I,S,j,$,F)):U>0&&U&64&&Q&&f.dynamicChildren?(Y(f.dynamicChildren,Q,k,I,S,j,$),(m.key!=null||I&&m===I.subTree)&&vo(f,m,!0)):ve(f,m,k,J,I,S,j,$,F)},ee=(f,m,k,L,I,S,j,$,F)=>{m.slotScopeIds=$,f==null?m.shapeFlag&512?I.ctx.activate(m,k,L,j,F):G(m,k,L,I,S,j,F):Se(f,m,F)},G=(f,m,k,L,I,S,j)=>{const $=f.component=Mp(f,L,I);if(nn(f)&&($.ctx.renderer=N),Vp($),$.asyncDep){if(I&&I.registerDep($,he),!f.el){const F=$.subTree=Ae(et);g(null,F,m,k)}return}he($,f,m,k,I,S,j)},Se=(f,m,k)=>{const L=m.component=f.component;if(Wd(f,m,k))if(L.asyncDep&&!L.asyncResolved){_e(L,m,k);return}else L.next=m,jd(L.update),L.update();else m.el=f.el,L.vnode=m},he=(f,m,k,L,I,S,j)=>{const $=()=>{if(f.isMounted){let{next:J,bu:U,u:Q,parent:Z,vnode:le}=f,we=J,ye;qt(f,!1),J?(J.el=le.el,_e(f,J,j)):J=le,U&&ua(U),(ye=J.props&&J.props.onVnodeBeforeUpdate)&&Ye(ye,Z,J,le),qt(f,!0);const Pe=da(f),lt=f.subTree;f.subTree=Pe,w(lt,Pe,p(lt.el),A(lt),f,I,S),J.el=Pe.el,we===null&&Kd(f,Pe.el),Q&&Be(Q,I),(ye=J.props&&J.props.onVnodeUpdated)&&Be(()=>Ye(ye,Z,J,le),I)}else{let J;const{el:U,props:Q}=m,{bm:Z,m:le,parent:we}=f,ye=Ml(m);if(qt(f,!1),Z&&ua(Z),!ye&&(J=Q&&Q.onVnodeBeforeMount)&&Ye(J,we,m),qt(f,!0),U&&fe){const Pe=()=>{f.subTree=da(f),fe(U,f.subTree,f,I,null)};ye?m.type.__asyncLoader().then(()=>!f.isUnmounted&&Pe()):Pe()}else{const Pe=f.subTree=da(f);w(null,Pe,k,L,f,I,S),m.el=Pe.el}if(le&&Be(le,I),!ye&&(J=Q&&Q.onVnodeMounted)){const Pe=m;Be(()=>Ye(J,we,Pe),I)}(m.shapeFlag&256||we&&Ml(we.vnode)&&we.vnode.shapeFlag&256)&&f.a&&Be(f.a,I),f.isMounted=!0,m=k=L=null}},F=f.effect=new oi($,()=>Kn(D),f.scope),D=f.update=()=>F.run();D.id=f.uid,qt(f,!0),D()},_e=(f,m,k)=>{m.component=f;const L=f.vnode.props;f.vnode=m,f.next=null,bp(f,m.props,L,k),wp(f,m.children,k),El(),sr(),xl()},ve=(f,m,k,L,I,S,j,$,F=!1)=>{const D=f&&f.children,J=f?f.shapeFlag:0,U=m.children,{patchFlag:Q,shapeFlag:Z}=m;if(Q>0){if(Q&128){It(D,U,k,L,I,S,j,$,F);return}else if(Q&256){bt(D,U,k,L,I,S,j,$,F);return}}Z&8?(J&16&&De(D,I,S),U!==D&&d(k,U)):J&16?Z&16?It(D,U,k,L,I,S,j,$,F):De(D,I,S,!0):(J&8&&d(k,""),Z&16&&T(U,k,L,I,S,j,$,F))},bt=(f,m,k,L,I,S,j,$,F)=>{f=f||cl,m=m||cl;const D=f.length,J=m.length,U=Math.min(D,J);let Q;for(Q=0;QJ?De(f,I,S,!0,!1,U):T(m,k,L,I,S,j,$,F,U)},It=(f,m,k,L,I,S,j,$,F)=>{let D=0;const J=m.length;let U=f.length-1,Q=J-1;for(;D<=U&&D<=Q;){const Z=f[D],le=m[D]=F?St(m[D]):nt(m[D]);if(Wt(Z,le))w(Z,le,k,null,I,S,j,$,F);else break;D++}for(;D<=U&&D<=Q;){const Z=f[U],le=m[Q]=F?St(m[Q]):nt(m[Q]);if(Wt(Z,le))w(Z,le,k,null,I,S,j,$,F);else break;U--,Q--}if(D>U){if(D<=Q){const Z=Q+1,le=ZQ)for(;D<=U;)je(f[D],I,S,!0),D++;else{const Z=D,le=D,we=new Map;for(D=le;D<=Q;D++){const Ge=m[D]=F?St(m[D]):nt(m[D]);Ge.key!=null&&we.set(Ge.key,D)}let ye,Pe=0;const lt=Q-le+1;let nl=!1,Ji=0;const Pl=new Array(lt);for(D=0;D=lt){je(Ge,I,S,!0);continue}let ht;if(Ge.key!=null)ht=we.get(Ge.key);else for(ye=le;ye<=Q;ye++)if(Pl[ye-le]===0&&Wt(Ge,m[ye])){ht=ye;break}ht===void 0?je(Ge,I,S,!0):(Pl[ht-le]=D+1,ht>=Ji?Ji=ht:nl=!0,w(Ge,m[ht],k,null,I,S,j,$,F),Pe++)}const Qi=nl?Tp(Pl):cl;for(ye=Qi.length-1,D=lt-1;D>=0;D--){const Ge=le+D,ht=m[Ge],Yi=Ge+1{const{el:S,type:j,transition:$,children:F,shapeFlag:D}=f;if(D&6){pt(f.component.subTree,m,k,L);return}if(D&128){f.suspense.move(m,k,L);return}if(D&64){j.move(f,m,k,N);return}if(j===Ke){n(S,m,k);for(let U=0;U$.enter(S),I);else{const{leave:U,delayLeave:Q,afterLeave:Z}=$,le=()=>n(S,m,k),we=()=>{U(S,()=>{le(),Z&&Z()})};Q?Q(S,le,we):we()}else n(S,m,k)},je=(f,m,k,L=!1,I=!1)=>{const{type:S,props:j,ref:$,children:F,dynamicChildren:D,shapeFlag:J,patchFlag:U,dirs:Q}=f;if($!=null&&jn($,null,k,f,!0),J&256){m.ctx.deactivate(f);return}const Z=J&1&&Q,le=!Ml(f);let we;if(le&&(we=j&&j.onVnodeBeforeUnmount)&&Ye(we,m,f),J&6)mn(f.component,k,L);else{if(J&128){f.suspense.unmount(k,L);return}Z&&vt(f,null,m,"beforeUnmount"),J&64?f.type.remove(f,m,k,I,N,L):D&&(S!==Ke||U>0&&U&64)?De(D,m,k,!1,!0):(S===Ke&&U&384||!I&&J&16)&&De(F,m,k),L&&tl(f)}(le&&(we=j&&j.onVnodeUnmounted)||Z)&&Be(()=>{we&&Ye(we,m,f),Z&&vt(f,null,m,"unmounted")},k)},tl=f=>{const{type:m,el:k,anchor:L,transition:I}=f;if(m===Ke){ll(k,L);return}if(m===Fl){C(f);return}const S=()=>{a(k),I&&!I.persisted&&I.afterLeave&&I.afterLeave()};if(f.shapeFlag&1&&I&&!I.persisted){const{leave:j,delayLeave:$}=I,F=()=>j(k,S);$?$(f.el,S,F):F()}else S()},ll=(f,m)=>{let k;for(;f!==m;)k=h(f),a(f),f=k;a(m)},mn=(f,m,k)=>{const{bum:L,scope:I,update:S,subTree:j,um:$}=f;L&&ua(L),I.stop(),S&&(S.active=!1,je(j,f,m,k)),$&&Be($,m),Be(()=>{f.isUnmounted=!0},m),m&&m.pendingBranch&&!m.isUnmounted&&f.asyncDep&&!f.asyncResolved&&f.suspenseId===m.pendingId&&(m.deps--,m.deps===0&&m.resolve())},De=(f,m,k,L=!1,I=!1,S=0)=>{for(let j=S;jf.shapeFlag&6?A(f.component.subTree):f.shapeFlag&128?f.suspense.next():h(f.anchor||f.el),z=(f,m,k)=>{f==null?m._vnode&&je(m._vnode,null,null,!0):w(m._vnode||null,f,m,null,null,null,k),sr(),Mn(),m._vnode=f},N={p:w,um:je,m:pt,r:tl,mt:G,mc:T,pc:ve,pbc:Y,n:A,o:e};let K,fe;return t&&([K,fe]=t(N)),{render:z,hydrate:K,createApp:mp(z,K)}}function qt({effect:e,update:t},l){e.allowRecurse=t.allowRecurse=l}function vo(e,t,l=!1){const n=e.children,a=t.children;if(X(n)&&X(a))for(let i=0;i>1,e[l[o]]0&&(t[n]=l[i-1]),l[i]=n)}}for(i=l.length,r=l[i-1];i-- >0;)l[i]=r,r=t[r];return l}const Ap=e=>e.__isTeleport,Ke=Symbol.for("v-fgt"),fl=Symbol.for("v-txt"),et=Symbol.for("v-cmt"),Fl=Symbol.for("v-stc"),Nl=[];let rt=null;function Ip(e=!1){Nl.push(rt=e?null:[])}function Pp(){Nl.pop(),rt=Nl[Nl.length-1]||null}let Kl=1;function br(e){Kl+=e}function fo(e){return e.dynamicChildren=Kl>0?rt||cl:null,Pp(),Kl>0&&rt&&rt.push(e),e}function ag(e,t,l,n,a,i){return fo(mo(e,t,l,n,a,i,!0))}function Op(e,t,l,n,a){return fo(Ae(e,t,l,n,a,!0))}function Na(e){return e?e.__v_isVNode===!0:!1}function Wt(e,t){return e.type===t.type&&e.key===t.key}const Xn="__vInternal",go=({key:e})=>e??null,Rn=({ref:e,ref_key:t,ref_for:l})=>(typeof e=="number"&&(e=""+e),e!=null?ae(e)||Oe(e)||te(e)?{i:Xe,r:e,k:t,f:!!l}:e:null);function mo(e,t=null,l=null,n=0,a=null,i=e===Ke?0:1,r=!1,o=!1){const c={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&go(t),ref:t&&Rn(t),scopeId:Qs,slotScopeIds:null,children:l,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:i,patchFlag:n,dynamicProps:a,dynamicChildren:null,appContext:null,ctx:Xe};return o?(ki(c,l),i&128&&e.normalize(c)):l&&(c.shapeFlag|=ae(l)?8:16),Kl>0&&!r&&rt&&(c.patchFlag>0||i&6)&&c.patchFlag!==32&&rt.push(c),c}const Ae=Cp;function Cp(e,t=null,l=null,n=0,a=null,i=!1){if((!e||e===op)&&(e=et),Na(e)){const o=jt(e,t,!0);return l&&ki(o,l),Kl>0&&!i&&rt&&(o.shapeFlag&6?rt[rt.indexOf(e)]=o:rt.push(o)),o.patchFlag|=-2,o}if(zp(e)&&(e=e.__vccOpts),t){t=Rp(t);let{class:o,style:c}=t;o&&!ae(o)&&(t.class=ri(o)),Te(c)&&(Bs(c)&&!X(c)&&(c=Ie({},c)),t.style=ii(c))}const r=ae(e)?1:Jd(e)?128:Ap(e)?64:Te(e)?4:te(e)?2:0;return mo(e,t,l,n,a,r,i,!0)}function Rp(e){return e?Bs(e)||Xn in e?Ie({},e):e:null}function jt(e,t,l=!1){const{props:n,ref:a,patchFlag:i,children:r}=e,o=t?Sp(n||{},t):n;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:o,key:o&&go(o),ref:t&&t.ref?l&&a?X(a)?a.concat(Rn(t)):[a,Rn(t)]:Rn(t):a,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:r,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==Ke?i===-1?16:i|16:i,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&jt(e.ssContent),ssFallback:e.ssFallback&&jt(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce}}function yo(e=" ",t=0){return Ae(fl,null,e,t)}function ig(e,t){const l=Ae(Fl,null,e);return l.staticCount=t,l}function rg(e="",t=!1){return t?(Ip(),Op(et,null,e)):Ae(et,null,e)}function nt(e){return e==null||typeof e=="boolean"?Ae(et):X(e)?Ae(Ke,null,e.slice()):typeof e=="object"?St(e):Ae(fl,null,String(e))}function St(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:jt(e)}function ki(e,t){let l=0;const{shapeFlag:n}=e;if(t==null)t=null;else if(X(t))l=16;else if(typeof t=="object")if(n&65){const a=t.default;a&&(a._c&&(a._d=!1),ki(e,a()),a._c&&(a._d=!0));return}else{l=32;const a=t._;!a&&!(Xn in t)?t._ctx=Xe:a===3&&Xe&&(Xe.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else te(t)?(t={default:t,_ctx:Xe},l=32):(t=String(t),n&64?(l=16,t=[yo(t)]):l=8);e.children=t,e.shapeFlag|=l}function Sp(...e){const t={};for(let l=0;lCe||Xe;let wi,al,_r="__VUE_INSTANCE_SETTERS__";(al=Oa()[_r])||(al=Oa()[_r]=[]),al.push(e=>Ce=e),wi=e=>{al.length>1?al.forEach(t=>t(e)):al[0](e)};const gl=e=>{wi(e),e.scope.on()},Qt=()=>{Ce&&Ce.scope.off(),wi(null)};function bo(e){return e.vnode.shapeFlag&4}let ml=!1;function Vp(e,t=!1){ml=t;const{props:l,children:n}=e.vnode,a=bo(e);yp(e,l,a,t),kp(e,n);const i=a?Fp(e,t):void 0;return ml=!1,i}function Fp(e,t){const l=e.type;e.accessCache=Object.create(null),e.proxy=zs(new Proxy(e.ctx,up));const{setup:n}=l;if(n){const a=e.setupContext=n.length>1?jp(e):null;gl(e),El();const i=Vt(n,e,0,[e.props,a]);if(xl(),Qt(),As(i)){if(i.then(Qt,Qt),t)return i.then(r=>{kr(e,r,t)}).catch(r=>{ln(r,e,0)});e.asyncDep=i}else kr(e,i,t)}else _o(e,t)}function kr(e,t,l){te(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:Te(t)&&(e.setupState=qs(t)),_o(e,l)}let wr;function _o(e,t,l){const n=e.type;if(!e.render){if(!t&&wr&&!n.render){const a=n.template||bi(e).template;if(a){const{isCustomElement:i,compilerOptions:r}=e.appContext.config,{delimiters:o,compilerOptions:c}=n,u=Ie(Ie({isCustomElement:i,delimiters:o},r),c);n.render=wr(a,u)}}e.render=n.render||st}gl(e),El(),dp(e),xl(),Qt()}function Np(e){return e.attrsProxy||(e.attrsProxy=new Proxy(e.attrs,{get(t,l){return qe(e,"get","$attrs"),t[l]}}))}function jp(e){const t=l=>{e.exposed=l||{}};return{get attrs(){return Np(e)},slots:e.slots,emit:e.emit,expose:t}}function Ei(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(qs(zs(e.exposed)),{get(t,l){if(l in t)return t[l];if(l in Vl)return Vl[l](e)},has(t,l){return l in t||l in Vl}}))}function Bp(e,t=!0){return te(e)?e.displayName||e.name:e.name||t&&e.__name}function zp(e){return te(e)&&"__vccOpts"in e}const E=(e,t)=>Vd(e,t,ml);function s(e,t,l){const n=arguments.length;return n===2?Te(t)&&!X(t)?Na(t)?Ae(e,null,[t]):Ae(e,t):Ae(e,null,t):(n>3?l=Array.prototype.slice.call(arguments,2):n===3&&Na(l)&&(l=[l]),Ae(e,t,l))}const Hp=Symbol.for("v-scx"),qp=()=>me(Hp),Up="3.3.4",Gp="http://www.w3.org/2000/svg",Kt=typeof document<"u"?document:null,Er=Kt&&Kt.createElement("template"),Wp={insert:(e,t,l)=>{t.insertBefore(e,l||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,l,n)=>{const a=t?Kt.createElementNS(Gp,e):Kt.createElement(e,l?{is:l}:void 0);return e==="select"&&n&&n.multiple!=null&&a.setAttribute("multiple",n.multiple),a},createText:e=>Kt.createTextNode(e),createComment:e=>Kt.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Kt.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,l,n,a,i){const r=l?l.previousSibling:t.lastChild;if(a&&(a===i||a.nextSibling))for(;t.insertBefore(a.cloneNode(!0),l),!(a===i||!(a=a.nextSibling)););else{Er.innerHTML=n?`${e}`:e;const o=Er.content;if(n){const c=o.firstChild;for(;c.firstChild;)o.appendChild(c.firstChild);o.removeChild(c)}t.insertBefore(o,l)}return[r?r.nextSibling:t.firstChild,l?l.previousSibling:t.lastChild]}};function Kp(e,t,l){const n=e._vtc;n&&(t=(t?[t,...n]:[...n]).join(" ")),t==null?e.removeAttribute("class"):l?e.setAttribute("class",t):e.className=t}function Jp(e,t,l){const n=e.style,a=ae(l);if(l&&!a){if(t&&!ae(t))for(const i in t)l[i]==null&&ja(n,i,"");for(const i in l)ja(n,i,l[i])}else{const i=n.display;a?t!==l&&(n.cssText=l):t&&e.removeAttribute("style"),"_vod"in e&&(n.display=i)}}const xr=/\s*!important$/;function ja(e,t,l){if(X(l))l.forEach(n=>ja(e,t,n));else if(l==null&&(l=""),t.startsWith("--"))e.setProperty(t,l);else{const n=Qp(e,t);xr.test(l)?e.setProperty(wl(n),l.replace(xr,""),"important"):e[n]=l}}const Lr=["Webkit","Moz","ms"],fa={};function Qp(e,t){const l=fa[t];if(l)return l;let n=tt(t);if(n!=="filter"&&n in e)return fa[t]=n;n=en(n);for(let a=0;aga||(n0.then(()=>ga=0),ga=Date.now());function i0(e,t){const l=n=>{if(!n._vts)n._vts=Date.now();else if(n._vts<=l.attached)return;Ze(r0(n,l.value),t,5,[n])};return l.value=e,l.attached=a0(),l}function r0(e,t){if(X(t)){const l=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{l.call(e),e._stopped=!0},t.map(n=>a=>!a._stopped&&n&&n(a))}else return t}const Ir=/^on[a-z]/,s0=(e,t,l,n,a=!1,i,r,o,c)=>{t==="class"?Kp(e,n,a):t==="style"?Jp(e,l,n):Zl(t)?ti(t)||t0(e,t,l,n,r):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):o0(e,t,n,a))?Xp(e,t,n,i,r,o,c):(t==="true-value"?e._trueValue=n:t==="false-value"&&(e._falseValue=n),Yp(e,t,n,a))};function o0(e,t,l,n){return n?!!(t==="innerHTML"||t==="textContent"||t in e&&Ir.test(t)&&te(l)):t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA"||Ir.test(t)&&ae(l)?!1:t in e}const Ct="transition",Ol="animation",Bt=(e,{slots:t})=>s(Xd,wo(e),t);Bt.displayName="Transition";const ko={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},c0=Bt.props=Ie({},to,ko),Ut=(e,t=[])=>{X(e)?e.forEach(l=>l(...t)):e&&e(...t)},Pr=e=>e?X(e)?e.some(t=>t.length>1):e.length>1:!1;function wo(e){const t={};for(const H in e)H in ko||(t[H]=e[H]);if(e.css===!1)return t;const{name:l="v",type:n,duration:a,enterFromClass:i=`${l}-enter-from`,enterActiveClass:r=`${l}-enter-active`,enterToClass:o=`${l}-enter-to`,appearFromClass:c=i,appearActiveClass:u=r,appearToClass:d=o,leaveFromClass:p=`${l}-leave-from`,leaveActiveClass:h=`${l}-leave-active`,leaveToClass:v=`${l}-leave-to`}=e,b=u0(a),w=b&&b[0],x=b&&b[1],{onBeforeEnter:g,onEnter:_,onEnterCancelled:P,onLeave:C,onLeaveCancelled:B,onBeforeAppear:R=g,onAppear:V=_,onAppearCancelled:T=P}=t,q=(H,ee,G)=>{Rt(H,ee?d:o),Rt(H,ee?u:r),G&&G()},Y=(H,ee)=>{H._isLeaving=!1,Rt(H,p),Rt(H,v),Rt(H,h),ee&&ee()},ne=H=>(ee,G)=>{const Se=H?V:_,he=()=>q(ee,H,G);Ut(Se,[ee,he]),Or(()=>{Rt(ee,H?c:i),kt(ee,H?d:o),Pr(Se)||Cr(ee,n,w,he)})};return Ie(t,{onBeforeEnter(H){Ut(g,[H]),kt(H,i),kt(H,r)},onBeforeAppear(H){Ut(R,[H]),kt(H,c),kt(H,u)},onEnter:ne(!1),onAppear:ne(!0),onLeave(H,ee){H._isLeaving=!0;const G=()=>Y(H,ee);kt(H,p),xo(),kt(H,h),Or(()=>{H._isLeaving&&(Rt(H,p),kt(H,v),Pr(C)||Cr(H,n,x,G))}),Ut(C,[H,G])},onEnterCancelled(H){q(H,!1),Ut(P,[H])},onAppearCancelled(H){q(H,!0),Ut(T,[H])},onLeaveCancelled(H){Y(H),Ut(B,[H])}})}function u0(e){if(e==null)return null;if(Te(e))return[ma(e.enter),ma(e.leave)];{const t=ma(e);return[t,t]}}function ma(e){return Gu(e)}function kt(e,t){t.split(/\s+/).forEach(l=>l&&e.classList.add(l)),(e._vtc||(e._vtc=new Set)).add(t)}function Rt(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.remove(n));const{_vtc:l}=e;l&&(l.delete(t),l.size||(e._vtc=void 0))}function Or(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let d0=0;function Cr(e,t,l,n){const a=e._endId=++d0,i=()=>{a===e._endId&&n()};if(l)return setTimeout(i,l);const{type:r,timeout:o,propCount:c}=Eo(e,t);if(!r)return n();const u=r+"end";let d=0;const p=()=>{e.removeEventListener(u,h),i()},h=v=>{v.target===e&&++d>=c&&p()};setTimeout(()=>{d(l[b]||"").split(", "),a=n(`${Ct}Delay`),i=n(`${Ct}Duration`),r=Rr(a,i),o=n(`${Ol}Delay`),c=n(`${Ol}Duration`),u=Rr(o,c);let d=null,p=0,h=0;t===Ct?r>0&&(d=Ct,p=r,h=i.length):t===Ol?u>0&&(d=Ol,p=u,h=c.length):(p=Math.max(r,u),d=p>0?r>u?Ct:Ol:null,h=d?d===Ct?i.length:c.length:0);const v=d===Ct&&/\b(transform|all)(,|$)/.test(n(`${Ct}Property`).toString());return{type:d,timeout:p,propCount:h,hasTransform:v}}function Rr(e,t){for(;e.lengthSr(l)+Sr(e[n])))}function Sr(e){return Number(e.slice(0,-1).replace(",","."))*1e3}function xo(){return document.body.offsetHeight}const Lo=new WeakMap,To=new WeakMap,Ao={name:"TransitionGroup",props:Ie({},c0,{tag:String,moveClass:String}),setup(e,{slots:t}){const l=Xt(),n=eo();let a,i;return ao(()=>{if(!a.length)return;const r=e.moveClass||`${e.name||"v"}-move`;if(!m0(a[0].el,l.vnode.el,r))return;a.forEach(v0),a.forEach(f0);const o=a.filter(g0);xo(),o.forEach(c=>{const u=c.el,d=u.style;kt(u,r),d.transform=d.webkitTransform=d.transitionDuration="";const p=u._moveCb=h=>{h&&h.target!==u||(!h||/transform$/.test(h.propertyName))&&(u.removeEventListener("transitionend",p),u._moveCb=null,Rt(u,r))};u.addEventListener("transitionend",p)})}),()=>{const r=re(e),o=wo(r);let c=r.tag||Ke;a=i,i=t.default?yi(t.default()):[];for(let u=0;udelete e.mode;Ao.props;const h0=Ao;function v0(e){const t=e.el;t._moveCb&&t._moveCb(),t._enterCb&&t._enterCb()}function f0(e){To.set(e,e.el.getBoundingClientRect())}function g0(e){const t=Lo.get(e),l=To.get(e),n=t.left-l.left,a=t.top-l.top;if(n||a){const i=e.el.style;return i.transform=i.webkitTransform=`translate(${n}px,${a}px)`,i.transitionDuration="0s",e}}function m0(e,t,l){const n=e.cloneNode();e._vtc&&e._vtc.forEach(r=>{r.split(/\s+/).forEach(o=>o&&n.classList.remove(o))}),l.split(/\s+/).forEach(r=>r&&n.classList.add(r)),n.style.display="none";const a=t.nodeType===1?t:t.parentNode;a.appendChild(n);const{hasTransform:i}=Eo(n);return a.removeChild(n),i}const y0=Ie({patchProp:s0},Wp);let ya,Dr=!1;function b0(){return ya=Dr?ya:xp(y0),Dr=!0,ya}const _0=(...e)=>{const t=b0().createApp(...e),{mount:l}=t;return t.mount=n=>{const a=k0(n);if(a)return l(a,!0,a instanceof SVGElement)},t};function k0(e){return ae(e)?document.querySelector(e):e}const w0={"v-8daa1a0e":()=>y(()=>import("./index.html-9c477b31.js"),[]).then(({data:e})=>e),"v-22a39d25":()=>y(()=>import("./about.html-b3827bb1.js"),[]).then(({data:e})=>e),"v-30a50b00":()=>y(()=>import("./a-vuepress2-entertaining-video.html-c410f2a7.js"),[]).then(({data:e})=>e),"v-0481cc80":()=>y(()=>import("./a-warning-from-navicat.html-d1243c4d.js"),[]).then(({data:e})=>e),"v-31eac486":()=>y(()=>import("./about-arm-things-you-need-to-know.html-b26fd653.js"),[]).then(({data:e})=>e),"v-79e139f8":()=>y(()=>import("./beyond-utf8-do-you-know-utf8mb4-and-collation.html-77f7e2a5.js"),[]).then(({data:e})=>e),"v-7a74360a":()=>y(()=>import("./claude-ai-in-action-extract-info-from-html.html-cd27a8d3.js"),[]).then(({data:e})=>e),"v-76f251d2":()=>y(()=>import("./copy-code-may-not-be-guilty.html-52cea58d.js"),[]).then(({data:e})=>e),"v-f47f129e":()=>y(()=>import("./dont-try-to-argue-with-a-sb.html-c2c1581f.js"),[]).then(({data:e})=>e),"v-fd7b7f92":()=>y(()=>import("./iteration-retrospective-of-sanyuan.html-b44a88d2.js"),[]).then(({data:e})=>e),"v-81a71778":()=>y(()=>import("./leverage-ai-to-boost-coding-productivity.html-0894834e.js"),[]).then(({data:e})=>e),"v-5c48d497":()=>y(()=>import("./reflections-on-a-speech-by-cto-of-microsoft-china.html-66a9c3f3.js"),[]).then(({data:e})=>e),"v-4f919602":()=>y(()=>import("./testing-environments-should-be-consistent-with-production-environments.html-009cb1c4.js"),[]).then(({data:e})=>e),"v-1e305501":()=>y(()=>import("./things-I-have-to-vent-about-vue.html-9c4bcda2.js"),[]).then(({data:e})=>e),"v-404740fa":()=>y(()=>import("./use-claude2-instead-of-chatgpt.html-d4f72795.js"),[]).then(({data:e})=>e),"v-f1efc11c":()=>y(()=>import("./vim-creator-pass-away.html-c4a8f7ca.js"),[]).then(({data:e})=>e),"v-414fd2b2":()=>y(()=>import("./what-is-the-difference-between-sh-and-bash.html-38f57eba.js"),[]).then(({data:e})=>e),"v-bd2b5fe8":()=>y(()=>import("./you-dont-need-to-add-tenant_id-to-every-table.html-d78e0fcf.js"),[]).then(({data:e})=>e),"v-971cc7fe":()=>y(()=>import("./contemporary-college-english-1.html-49632c93.js"),[]).then(({data:e})=>e),"v-93b316c0":()=>y(()=>import("./contemporary-college-english-2.html-675f1cf2.js"),[]).then(({data:e})=>e),"v-90496582":()=>y(()=>import("./contemporary-college-english-3.html-06118028.js"),[]).then(({data:e})=>e),"v-8cdfb444":()=>y(()=>import("./contemporary-college-english-4.html-5a5508d7.js"),[]).then(({data:e})=>e),"v-89760306":()=>y(()=>import("./contemporary-college-english-5.html-c752988e.js"),[]).then(({data:e})=>e),"v-860c51c8":()=>y(()=>import("./contemporary-college-english-6.html-cc0d8518.js"),[]).then(({data:e})=>e),"v-246f4f17":()=>y(()=>import("./everyone-can-learn-english-1-overview.html-52c2cad5.js"),[]).then(({data:e})=>e),"v-3ac0474c":()=>y(()=>import("./everyone-can-learn-english-2-pronunciation.html-f7ca6655.js"),[]).then(({data:e})=>e),"v-58a409d7":()=>y(()=>import("./everyone-can-learn-english-3-words.html-c288ffa2.js"),[]).then(({data:e})=>e),"v-788d194a":()=>y(()=>import("./everyone-can-learn-english-4-listening-and-speaking.html-bcf756a7.js"),[]).then(({data:e})=>e),"v-ae153a4e":()=>y(()=>import("./everyone-can-learn-english-5-reading-and-writing.html-630438e5.js"),[]).then(({data:e})=>e),"v-d42db13c":()=>y(()=>import("./how-to-self-evaluate-english-level.html-cc5748d1.js"),[]).then(({data:e})=>e),"v-6ed7d996":()=>y(()=>import("./learning-7000-words-task-completed.html-c7c6176d.js"),[]).then(({data:e})=>e),"v-221efd1f":()=>y(()=>import("./let-chatgpt-be-your-foreign-language-teacher.html-18abfed2.js"),[]).then(({data:e})=>e),"v-72e84a92":()=>y(()=>import("./old-articles.html-6079e753.js"),[]).then(({data:e})=>e),"v-7320140c":()=>y(()=>import("./performance-optimization-in-action.html-0aff1283.js"),[]).then(({data:e})=>e),"v-1e5872c4":()=>y(()=>import("./git-best-pratices.html-9f9a9e0e.js"),[]).then(({data:e})=>e),"v-48e494ef":()=>y(()=>import("./git-definitive-guide-to-merge-code.html-88801d1b.js"),[]).then(({data:e})=>e),"v-60021cbc":()=>y(()=>import("./git-history-two-tricks-in-idea.html-0b65486a.js"),[]).then(({data:e})=>e),"v-642eaaea":()=>y(()=>import("./git-useful-commands.html-3d5dd23d.js"),[]).then(({data:e})=>e),"v-4008ff77":()=>y(()=>import("./gitlab-ci.html-99056a64.js"),[]).then(({data:e})=>e),"v-0fbf5fdc":()=>y(()=>import("./rethinking-git-flow.html-60cc8caf.js"),[]).then(({data:e})=>e),"v-071be141":()=>y(()=>import("./use-command-line-tool-to-manage-gitlab-merge-request.html-1f237536.js"),[]).then(({data:e})=>e),"v-86a15cb6":()=>y(()=>import("./Resolving-Common-Problems-in-IntelliJ-IDEA.html-73ad8a4f.js"),[]).then(({data:e})=>e),"v-6dc18ec6":()=>y(()=>import("./Resolving-Common-Problems-in-Maven.md.html-f77903d5.js"),[]).then(({data:e})=>e),"v-538a98d7":()=>y(()=>import("./avoid-sending-password-in-plaintext.html-7173da3b.js"),[]).then(({data:e})=>e),"v-2f6ae09c":()=>y(()=>import("./check-if-name-exists.html-20410c4b.js"),[]).then(({data:e})=>e),"v-b5a78a7a":()=>y(()=>import("./common-practices-for-handling-excel.html-c351dd73.js"),[]).then(({data:e})=>e),"v-100d1814":()=>y(()=>import("./how-to-convert-snapshot-into-release-jar-without-source-code.html-f9386128.js"),[]).then(({data:e})=>e),"v-d767e98e":()=>y(()=>import("./recommend-practices-for-collections-naming-convention.html-ba39413b.js"),[]).then(({data:e})=>e),"v-f5471cb0":()=>y(()=>import("./recommend-practices-for-query-by-date-range.html-c4cb8fd3.js"),[]).then(({data:e})=>e),"v-57d9b582":()=>y(()=>import("./recommend-practices-for-writing-good-functions.html-6cc4ed1e.js"),[]).then(({data:e})=>e),"v-2ba0b01e":()=>y(()=>import("./using-enum-in-java.html-db9b25e7.js"),[]).then(({data:e})=>e),"v-386e94f1":()=>y(()=>import("./which-one-is-better-Boolean-or-boolean.html-03e8b2fa.js"),[]).then(({data:e})=>e),"v-0d6828c2":()=>y(()=>import("./which-one-is-better-forEach-or-map.html-16d69e48.js"),[]).then(({data:e})=>e),"v-1aafac08":()=>y(()=>import("./why-i-prefer-fastjson-instead-of-jackson.html-67cee556.js"),[]).then(({data:e})=>e),"v-ca672354":()=>y(()=>import("./why-is-it-so-hard-to-upgrade-dependencies.html-996256d6.js"),[]).then(({data:e})=>e),"v-143a8bce":()=>y(()=>import("./mysql-backup-case-study-mysqldump-in-action.html-73cda0a5.js"),[]).then(({data:e})=>e),"v-7e2c7a0c":()=>y(()=>import("./mysql-data-migration-case-study-add-auto-increment.html-6018956d.js"),[]).then(({data:e})=>e),"v-f297935a":()=>y(()=>import("./mysql-details-you-should-know-when-execute-sql-in-command-line.html-3a6f2dc3.js"),[]).then(({data:e})=>e),"v-5b37b3c6":()=>y(()=>import("./export-mysql-table-into-excel.html-3d06cd3e.js"),[]).then(({data:e})=>e),"v-113531b4":()=>y(()=>import("./unit-testing-overview.html-c6b939b4.js"),[]).then(({data:e})=>e),"v-65b23736":()=>y(()=>import("./use-RestAssured-for-api-testing.html-a06e24bd.js"),[]).then(({data:e})=>e),"v-c488ac58":()=>y(()=>import("./use-cypress-for-e2e-testing.html-2b65285a.js"),[]).then(({data:e})=>e),"v-efcacba2":()=>y(()=>import("./use-jest-for-test-driven-development.html-d2d469d3.js"),[]).then(({data:e})=>e),"v-0be1af08":()=>y(()=>import("./use-playwright-for-ui-testing.html-fba57f32.js"),[]).then(({data:e})=>e),"v-75616b85":()=>y(()=>import("./use-postman-for-api-testing.html-e4659ca6.js"),[]).then(({data:e})=>e),"v-17809471":()=>y(()=>import("./how-to-connect-to-internet.html-0a649e4e.js"),[]).then(({data:e})=>e),"v-3706649a":()=>y(()=>import("./404.html-f2d5dd87.js"),[]).then(({data:e})=>e),"v-79c9f96f":()=>y(()=>import("./index.html-6a79c9ec.js"),[]).then(({data:e})=>e),"v-43539db8":()=>y(()=>import("./index.html-8779bfb9.js"),[]).then(({data:e})=>e),"v-06198984":()=>y(()=>import("./index.html-6039efb3.js"),[]).then(({data:e})=>e),"v-74473916":()=>y(()=>import("./index.html-b483266e.js"),[]).then(({data:e})=>e),"v-14c69af4":()=>y(()=>import("./index.html-08f7d006.js"),[]).then(({data:e})=>e),"v-eb072ff4":()=>y(()=>import("./index.html-bdb5e891.js"),[]).then(({data:e})=>e),"v-63cd5dba":()=>y(()=>import("./index.html-6866fdfe.js"),[]).then(({data:e})=>e),"v-0df55bac":()=>y(()=>import("./index.html-b3692dcf.js"),[]).then(({data:e})=>e),"v-d440f426":()=>y(()=>import("./index.html-aff33757.js"),[]).then(({data:e})=>e),"v-5bc93818":()=>y(()=>import("./index.html-fe376c9d.js"),[]).then(({data:e})=>e),"v-744d024e":()=>y(()=>import("./index.html-7b393b72.js"),[]).then(({data:e})=>e),"v-e52c881c":()=>y(()=>import("./index.html-1bb998c5.js"),[]).then(({data:e})=>e),"v-154dc4c4":()=>y(()=>import("./index.html-fa3db4ea.js"),[]).then(({data:e})=>e),"v-01560935":()=>y(()=>import("./index.html-c98c047d.js"),[]).then(({data:e})=>e),"v-3d5315f8":()=>y(()=>import("./index.html-e4ea5f08.js"),[]).then(({data:e})=>e),"v-1b3ae9cf":()=>y(()=>import("./index.html-db9a5c1a.js"),[]).then(({data:e})=>e),"v-007c0ae2":()=>y(()=>import("./index.html-8811bd65.js"),[]).then(({data:e})=>e),"v-211f44ee":()=>y(()=>import("./index.html-5fa1740f.js"),[]).then(({data:e})=>e),"v-6106c001":()=>y(()=>import("./index.html-4fc033c7.js"),[]).then(({data:e})=>e),"v-1bee38ca":()=>y(()=>import("./index.html-c50be8cb.js"),[]).then(({data:e})=>e),"v-0da0abf9":()=>y(()=>import("./index.html-90b5caba.js"),[]).then(({data:e})=>e),"v-5a3e80fc":()=>y(()=>import("./index.html-96e262ec.js"),[]).then(({data:e})=>e),"v-01c1de5b":()=>y(()=>import("./index.html-3d1d7f95.js"),[]).then(({data:e})=>e),"v-29350809":()=>y(()=>import("./index.html-fa48630e.js"),[]).then(({data:e})=>e),"v-50d6e023":()=>y(()=>import("./index.html-62782fea.js"),[]).then(({data:e})=>e),"v-0ca0efe6":()=>y(()=>import("./index.html-dc40d5ff.js"),[]).then(({data:e})=>e),"v-b310d42a":()=>y(()=>import("./index.html-5b2edaa1.js"),[]).then(({data:e})=>e),"v-13275df4":()=>y(()=>import("./index.html-15cad1de.js"),[]).then(({data:e})=>e),"v-28a1d8bf":()=>y(()=>import("./index.html-c22f0db4.js"),[]).then(({data:e})=>e),"v-60379330":()=>y(()=>import("./index.html-6d3cb5cc.js"),[]).then(({data:e})=>e),"v-245f5676":()=>y(()=>import("./index.html-44e80ad7.js"),[]).then(({data:e})=>e),"v-3b951558":()=>y(()=>import("./index.html-db761428.js"),[]).then(({data:e})=>e),"v-48b3e46d":()=>y(()=>import("./index.html-4736b52e.js"),[]).then(({data:e})=>e)},E0=JSON.parse(`{"base":"/","lang":"zh-CN","title":"levy","description":"levy's blog","head":[["meta",{"name":"google-site-verification","content":"XSoaUnV59ACn-fVEvYre2y_5mka_7o_wEoMPBQpwo2M"}],["script",{"async":true,"src":"https://www.googletagmanager.com/gtag/js?id=G-6HEW6B1S6B"}],["script",{},["window.dataLayer = window.dataLayer || [];\\nfunction gtag(){dataLayer.push(arguments);}\\ngtag('js', new Date());\\ngtag('config','G-6HEW6B1S6B');"]],["link",{"rel":"alternate","type":"application/rss+xml","href":"https://levy.vip/rss.xml","title":"levy RSS Feed"}]],"locales":{}}`);var x0=([e,t,l])=>e==="meta"&&t.name?`${e}.${t.name}`:["title","base"].includes(e)?e:e==="template"&&t.id?`${e}.${t.id}`:JSON.stringify([e,t,l]),L0=e=>{const t=new Set,l=[];return e.forEach(n=>{const a=x0(n);t.has(a)||(t.add(a),l.push(n))}),l},T0=e=>e[e.length-1]==="/"||e.endsWith(".html")?e:`${e}/`,A0=e=>e.startsWith("ftp://"),Zt=e=>/^(https?:)?\/\//.test(e),I0=/.md((\?|#).*)?$/,Bn=(e,t="/")=>!!(Zt(e)||A0(e)||e.startsWith("/")&&!e.startsWith(t)&&!I0.test(e)),Io=e=>/^mailto:/.test(e),P0=e=>/^tel:/.test(e),xi=e=>Object.prototype.toString.call(e)==="[object Object]",Li=e=>e[e.length-1]==="/"?e.slice(0,-1):e,Po=e=>e[0]==="/"?e.slice(1):e,O0=(e,t)=>{const l=Object.keys(e).sort((n,a)=>{const i=a.split("/").length-n.split("/").length;return i!==0?i:a.length-n.length});for(const n of l)if(t.startsWith(n))return n;return"/"};const Oo={"v-8daa1a0e":O(()=>y(()=>import("./index.html-bd2fe08a.js"),["assets/index.html-bd2fe08a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-22a39d25":O(()=>y(()=>import("./about.html-1efe40f7.js"),["assets/about.html-1efe40f7.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-30a50b00":O(()=>y(()=>import("./a-vuepress2-entertaining-video.html-d7d94634.js"),["assets/a-vuepress2-entertaining-video.html-d7d94634.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0481cc80":O(()=>y(()=>import("./a-warning-from-navicat.html-dbede3aa.js"),["assets/a-warning-from-navicat.html-dbede3aa.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-31eac486":O(()=>y(()=>import("./about-arm-things-you-need-to-know.html-a719ca33.js"),["assets/about-arm-things-you-need-to-know.html-a719ca33.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-79e139f8":O(()=>y(()=>import("./beyond-utf8-do-you-know-utf8mb4-and-collation.html-d98b859a.js"),["assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-d98b859a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-7a74360a":O(()=>y(()=>import("./claude-ai-in-action-extract-info-from-html.html-47bc02a1.js"),["assets/claude-ai-in-action-extract-info-from-html.html-47bc02a1.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-76f251d2":O(()=>y(()=>import("./copy-code-may-not-be-guilty.html-5562ab70.js"),["assets/copy-code-may-not-be-guilty.html-5562ab70.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-f47f129e":O(()=>y(()=>import("./dont-try-to-argue-with-a-sb.html-5919dbb8.js"),["assets/dont-try-to-argue-with-a-sb.html-5919dbb8.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-fd7b7f92":O(()=>y(()=>import("./iteration-retrospective-of-sanyuan.html-8d424c1c.js"),["assets/iteration-retrospective-of-sanyuan.html-8d424c1c.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-81a71778":O(()=>y(()=>import("./leverage-ai-to-boost-coding-productivity.html-77d0fee4.js"),["assets/leverage-ai-to-boost-coding-productivity.html-77d0fee4.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5c48d497":O(()=>y(()=>import("./reflections-on-a-speech-by-cto-of-microsoft-china.html-49fb2b4d.js"),["assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-49fb2b4d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-4f919602":O(()=>y(()=>import("./testing-environments-should-be-consistent-with-production-environments.html-292c2a25.js"),["assets/testing-environments-should-be-consistent-with-production-environments.html-292c2a25.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1e305501":O(()=>y(()=>import("./things-I-have-to-vent-about-vue.html-44f6a97e.js"),["assets/things-I-have-to-vent-about-vue.html-44f6a97e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-404740fa":O(()=>y(()=>import("./use-claude2-instead-of-chatgpt.html-c55c251a.js"),["assets/use-claude2-instead-of-chatgpt.html-c55c251a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-f1efc11c":O(()=>y(()=>import("./vim-creator-pass-away.html-89c669cf.js"),["assets/vim-creator-pass-away.html-89c669cf.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-414fd2b2":O(()=>y(()=>import("./what-is-the-difference-between-sh-and-bash.html-f59545ff.js"),["assets/what-is-the-difference-between-sh-and-bash.html-f59545ff.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-bd2b5fe8":O(()=>y(()=>import("./you-dont-need-to-add-tenant_id-to-every-table.html-36585552.js"),["assets/you-dont-need-to-add-tenant_id-to-every-table.html-36585552.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-971cc7fe":O(()=>y(()=>import("./contemporary-college-english-1.html-e4e094da.js"),["assets/contemporary-college-english-1.html-e4e094da.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-93b316c0":O(()=>y(()=>import("./contemporary-college-english-2.html-47fef5cf.js"),["assets/contemporary-college-english-2.html-47fef5cf.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-90496582":O(()=>y(()=>import("./contemporary-college-english-3.html-80541a07.js"),["assets/contemporary-college-english-3.html-80541a07.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-8cdfb444":O(()=>y(()=>import("./contemporary-college-english-4.html-756c6e8b.js"),["assets/contemporary-college-english-4.html-756c6e8b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-89760306":O(()=>y(()=>import("./contemporary-college-english-5.html-5e519758.js"),["assets/contemporary-college-english-5.html-5e519758.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-860c51c8":O(()=>y(()=>import("./contemporary-college-english-6.html-3f6139cd.js"),["assets/contemporary-college-english-6.html-3f6139cd.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-246f4f17":O(()=>y(()=>import("./everyone-can-learn-english-1-overview.html-a4b93650.js"),["assets/everyone-can-learn-english-1-overview.html-a4b93650.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3ac0474c":O(()=>y(()=>import("./everyone-can-learn-english-2-pronunciation.html-796320a9.js"),["assets/everyone-can-learn-english-2-pronunciation.html-796320a9.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-58a409d7":O(()=>y(()=>import("./everyone-can-learn-english-3-words.html-d680e9c6.js"),["assets/everyone-can-learn-english-3-words.html-d680e9c6.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-788d194a":O(()=>y(()=>import("./everyone-can-learn-english-4-listening-and-speaking.html-8bf4c725.js"),["assets/everyone-can-learn-english-4-listening-and-speaking.html-8bf4c725.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-ae153a4e":O(()=>y(()=>import("./everyone-can-learn-english-5-reading-and-writing.html-cf6b1e9b.js"),["assets/everyone-can-learn-english-5-reading-and-writing.html-cf6b1e9b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-d42db13c":O(()=>y(()=>import("./how-to-self-evaluate-english-level.html-16ae55da.js"),["assets/how-to-self-evaluate-english-level.html-16ae55da.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-6ed7d996":O(()=>y(()=>import("./learning-7000-words-task-completed.html-f60d77c6.js"),["assets/learning-7000-words-task-completed.html-f60d77c6.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-221efd1f":O(()=>y(()=>import("./let-chatgpt-be-your-foreign-language-teacher.html-4287d21b.js"),["assets/let-chatgpt-be-your-foreign-language-teacher.html-4287d21b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-72e84a92":O(()=>y(()=>import("./old-articles.html-ec567119.js"),["assets/old-articles.html-ec567119.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-7320140c":O(()=>y(()=>import("./performance-optimization-in-action.html-fa684067.js"),["assets/performance-optimization-in-action.html-fa684067.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1e5872c4":O(()=>y(()=>import("./git-best-pratices.html-4b6532e6.js"),["assets/git-best-pratices.html-4b6532e6.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-48e494ef":O(()=>y(()=>import("./git-definitive-guide-to-merge-code.html-abe98718.js"),["assets/git-definitive-guide-to-merge-code.html-abe98718.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-60021cbc":O(()=>y(()=>import("./git-history-two-tricks-in-idea.html-41e54e71.js"),["assets/git-history-two-tricks-in-idea.html-41e54e71.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-642eaaea":O(()=>y(()=>import("./git-useful-commands.html-517e5b83.js"),["assets/git-useful-commands.html-517e5b83.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-4008ff77":O(()=>y(()=>import("./gitlab-ci.html-01cc0cbe.js"),["assets/gitlab-ci.html-01cc0cbe.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0fbf5fdc":O(()=>y(()=>import("./rethinking-git-flow.html-cd22e8fc.js"),["assets/rethinking-git-flow.html-cd22e8fc.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-071be141":O(()=>y(()=>import("./use-command-line-tool-to-manage-gitlab-merge-request.html-e01c2260.js"),["assets/use-command-line-tool-to-manage-gitlab-merge-request.html-e01c2260.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-86a15cb6":O(()=>y(()=>import("./Resolving-Common-Problems-in-IntelliJ-IDEA.html-ce1eab94.js"),["assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-ce1eab94.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-6dc18ec6":O(()=>y(()=>import("./Resolving-Common-Problems-in-Maven.md.html-9fd731f4.js"),["assets/Resolving-Common-Problems-in-Maven.md.html-9fd731f4.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-538a98d7":O(()=>y(()=>import("./avoid-sending-password-in-plaintext.html-e9e5371d.js"),["assets/avoid-sending-password-in-plaintext.html-e9e5371d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-2f6ae09c":O(()=>y(()=>import("./check-if-name-exists.html-488fca17.js"),["assets/check-if-name-exists.html-488fca17.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-b5a78a7a":O(()=>y(()=>import("./common-practices-for-handling-excel.html-d03a1dea.js"),["assets/common-practices-for-handling-excel.html-d03a1dea.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-100d1814":O(()=>y(()=>import("./how-to-convert-snapshot-into-release-jar-without-source-code.html-bbf44529.js"),["assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-bbf44529.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-d767e98e":O(()=>y(()=>import("./recommend-practices-for-collections-naming-convention.html-4ce11630.js"),["assets/recommend-practices-for-collections-naming-convention.html-4ce11630.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-f5471cb0":O(()=>y(()=>import("./recommend-practices-for-query-by-date-range.html-f4c04744.js"),["assets/recommend-practices-for-query-by-date-range.html-f4c04744.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-57d9b582":O(()=>y(()=>import("./recommend-practices-for-writing-good-functions.html-263e2d20.js"),["assets/recommend-practices-for-writing-good-functions.html-263e2d20.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-2ba0b01e":O(()=>y(()=>import("./using-enum-in-java.html-cb86be54.js"),["assets/using-enum-in-java.html-cb86be54.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-386e94f1":O(()=>y(()=>import("./which-one-is-better-Boolean-or-boolean.html-90421d51.js"),["assets/which-one-is-better-Boolean-or-boolean.html-90421d51.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0d6828c2":O(()=>y(()=>import("./which-one-is-better-forEach-or-map.html-7837b1cd.js"),["assets/which-one-is-better-forEach-or-map.html-7837b1cd.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1aafac08":O(()=>y(()=>import("./why-i-prefer-fastjson-instead-of-jackson.html-ef0d6e0e.js"),["assets/why-i-prefer-fastjson-instead-of-jackson.html-ef0d6e0e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-ca672354":O(()=>y(()=>import("./why-is-it-so-hard-to-upgrade-dependencies.html-8df8757e.js"),["assets/why-is-it-so-hard-to-upgrade-dependencies.html-8df8757e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-143a8bce":O(()=>y(()=>import("./mysql-backup-case-study-mysqldump-in-action.html-b5eeab0a.js"),["assets/mysql-backup-case-study-mysqldump-in-action.html-b5eeab0a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-7e2c7a0c":O(()=>y(()=>import("./mysql-data-migration-case-study-add-auto-increment.html-b3a99eec.js"),["assets/mysql-data-migration-case-study-add-auto-increment.html-b3a99eec.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-f297935a":O(()=>y(()=>import("./mysql-details-you-should-know-when-execute-sql-in-command-line.html-e11c76db.js"),["assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-e11c76db.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5b37b3c6":O(()=>y(()=>import("./export-mysql-table-into-excel.html-e4373920.js"),["assets/export-mysql-table-into-excel.html-e4373920.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-113531b4":O(()=>y(()=>import("./unit-testing-overview.html-3f5f616d.js"),["assets/unit-testing-overview.html-3f5f616d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-65b23736":O(()=>y(()=>import("./use-RestAssured-for-api-testing.html-db3b5b16.js"),["assets/use-RestAssured-for-api-testing.html-db3b5b16.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-c488ac58":O(()=>y(()=>import("./use-cypress-for-e2e-testing.html-450ffdcd.js"),["assets/use-cypress-for-e2e-testing.html-450ffdcd.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-efcacba2":O(()=>y(()=>import("./use-jest-for-test-driven-development.html-ed10dede.js"),["assets/use-jest-for-test-driven-development.html-ed10dede.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0be1af08":O(()=>y(()=>import("./use-playwright-for-ui-testing.html-f3b691a5.js"),["assets/use-playwright-for-ui-testing.html-f3b691a5.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-75616b85":O(()=>y(()=>import("./use-postman-for-api-testing.html-ed796a62.js"),["assets/use-postman-for-api-testing.html-ed796a62.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-17809471":O(()=>y(()=>import("./how-to-connect-to-internet.html-b65ec9df.js"),["assets/how-to-connect-to-internet.html-b65ec9df.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3706649a":O(()=>y(()=>import("./404.html-0b613605.js"),["assets/404.html-0b613605.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-79c9f96f":O(()=>y(()=>import("./index.html-a621f81c.js"),["assets/index.html-a621f81c.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-43539db8":O(()=>y(()=>import("./index.html-b895087a.js"),["assets/index.html-b895087a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-06198984":O(()=>y(()=>import("./index.html-9cbf5acb.js"),["assets/index.html-9cbf5acb.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-74473916":O(()=>y(()=>import("./index.html-408ce1a1.js"),["assets/index.html-408ce1a1.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-14c69af4":O(()=>y(()=>import("./index.html-4c220eec.js"),["assets/index.html-4c220eec.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-eb072ff4":O(()=>y(()=>import("./index.html-bfd1105e.js"),["assets/index.html-bfd1105e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-63cd5dba":O(()=>y(()=>import("./index.html-c6b00465.js"),["assets/index.html-c6b00465.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0df55bac":O(()=>y(()=>import("./index.html-c933b166.js"),["assets/index.html-c933b166.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-d440f426":O(()=>y(()=>import("./index.html-112bc49c.js"),["assets/index.html-112bc49c.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5bc93818":O(()=>y(()=>import("./index.html-43e4e5f7.js"),["assets/index.html-43e4e5f7.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-744d024e":O(()=>y(()=>import("./index.html-070b1600.js"),["assets/index.html-070b1600.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-e52c881c":O(()=>y(()=>import("./index.html-91f24fc6.js"),["assets/index.html-91f24fc6.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-154dc4c4":O(()=>y(()=>import("./index.html-98c939cd.js"),["assets/index.html-98c939cd.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-01560935":O(()=>y(()=>import("./index.html-fb514775.js"),["assets/index.html-fb514775.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3d5315f8":O(()=>y(()=>import("./index.html-515fd40f.js"),["assets/index.html-515fd40f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1b3ae9cf":O(()=>y(()=>import("./index.html-3a00d6ef.js"),["assets/index.html-3a00d6ef.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-007c0ae2":O(()=>y(()=>import("./index.html-14879732.js"),["assets/index.html-14879732.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-211f44ee":O(()=>y(()=>import("./index.html-9fa06e1f.js"),["assets/index.html-9fa06e1f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-6106c001":O(()=>y(()=>import("./index.html-88e3a8c0.js"),["assets/index.html-88e3a8c0.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1bee38ca":O(()=>y(()=>import("./index.html-7ad6c908.js"),["assets/index.html-7ad6c908.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0da0abf9":O(()=>y(()=>import("./index.html-55ab9a01.js"),["assets/index.html-55ab9a01.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5a3e80fc":O(()=>y(()=>import("./index.html-9b7d49c9.js"),["assets/index.html-9b7d49c9.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-01c1de5b":O(()=>y(()=>import("./index.html-e7a8926e.js"),["assets/index.html-e7a8926e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-29350809":O(()=>y(()=>import("./index.html-ae84f935.js"),["assets/index.html-ae84f935.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-50d6e023":O(()=>y(()=>import("./index.html-6cb930a5.js"),["assets/index.html-6cb930a5.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0ca0efe6":O(()=>y(()=>import("./index.html-896d34f9.js"),["assets/index.html-896d34f9.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-b310d42a":O(()=>y(()=>import("./index.html-d70e9d37.js"),["assets/index.html-d70e9d37.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-13275df4":O(()=>y(()=>import("./index.html-4d16e2b0.js"),["assets/index.html-4d16e2b0.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-28a1d8bf":O(()=>y(()=>import("./index.html-e53262f0.js"),["assets/index.html-e53262f0.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-60379330":O(()=>y(()=>import("./index.html-8bec8f40.js"),["assets/index.html-8bec8f40.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-245f5676":O(()=>y(()=>import("./index.html-4458b0dc.js"),["assets/index.html-4458b0dc.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3b951558":O(()=>y(()=>import("./index.html-e76096f8.js"),["assets/index.html-e76096f8.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-48b3e46d":O(()=>y(()=>import("./index.html-2a568803.js"),["assets/index.html-2a568803.js","assets/plugin-vue_export-helper-c27b6911.js"]))};var C0=Symbol(""),R0=W(w0),Co=Yt({key:"",path:"",title:"",lang:"",frontmatter:{},headers:[]}),Dt=W(Co),ue=()=>Dt,Ro=Symbol(""),Ee=()=>{const e=me(Ro);if(!e)throw new Error("usePageFrontmatter() is called without provider.");return e},So=Symbol(""),S0=()=>{const e=me(So);if(!e)throw new Error("usePageHead() is called without provider.");return e},D0=Symbol(""),Do=Symbol(""),$o=()=>{const e=me(Do);if(!e)throw new Error("usePageLang() is called without provider.");return e},Mo=Symbol(""),$0=()=>{const e=me(Mo);if(!e)throw new Error("usePageLayout() is called without provider.");return e},Ti=Symbol(""),mt=()=>{const e=me(Ti);if(!e)throw new Error("useRouteLocale() is called without provider.");return e},ol=W(E0),Vo=()=>ol,Fo=Symbol(""),rn=()=>{const e=me(Fo);if(!e)throw new Error("useSiteLocaleData() is called without provider.");return e},M0=Symbol(""),V0="Layout",F0="NotFound",wt=tn({resolveLayouts:e=>e.reduce((t,l)=>({...t,...l.layouts}),{}),resolvePageData:async e=>{const t=R0.value[e];return await(t==null?void 0:t())??Co},resolvePageFrontmatter:e=>e.frontmatter,resolvePageHead:(e,t,l)=>{const n=ae(t.description)?t.description:l.description,a=[...X(t.head)?t.head:[],...l.head,["title",{},e],["meta",{name:"description",content:n}]];return L0(a)},resolvePageHeadTitle:(e,t)=>[e.title,t.title].filter(l=>!!l).join(" | "),resolvePageLang:(e,t)=>e.lang||t.lang||"en-US",resolvePageLayout:(e,t)=>{let l;if(e.path){const n=e.frontmatter.layout;ae(n)?l=n:l=V0}else l=F0;return t[l]},resolveRouteLocale:(e,t)=>O0(e,t),resolveSiteLocaleData:(e,t)=>({...e,...e.locales[t]})}),Zn=M({name:"ClientOnly",setup(e,t){const l=W(!1);return ke(()=>{l.value=!0}),()=>{var n,a;return l.value?(a=(n=t.slots).default)==null?void 0:a.call(n):null}}}),No=M({name:"Content",props:{pageKey:{type:String,required:!1,default:""}},setup(e){const t=ue(),l=E(()=>Oo[e.pageKey||t.value.key]);return()=>l.value?s(l.value):s("div","404 Not Found")}}),dt=(e={})=>e,Le=e=>Zt(e)?e:`/${Po(e)}`;const N0={};/*! + * vue-router v4.2.4 + * (c) 2023 Eduardo San Martin Morote + * @license MIT + */const rl=typeof window<"u";function j0(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const ge=Object.assign;function ba(e,t){const l={};for(const n in t){const a=t[n];l[n]=ut(a)?a.map(e):e(a)}return l}const jl=()=>{},ut=Array.isArray,B0=/\/$/,z0=e=>e.replace(B0,"");function _a(e,t,l="/"){let n,a={},i="",r="";const o=t.indexOf("#");let c=t.indexOf("?");return o=0&&(c=-1),c>-1&&(n=t.slice(0,c),i=t.slice(c+1,o>-1?o:t.length),a=e(i)),o>-1&&(n=n||t.slice(0,o),r=t.slice(o,t.length)),n=G0(n??t,l),{fullPath:n+(i&&"?")+i+r,path:n,query:a,hash:r}}function H0(e,t){const l=t.query?e(t.query):"";return t.path+(l&&"?")+l+(t.hash||"")}function $r(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function q0(e,t,l){const n=t.matched.length-1,a=l.matched.length-1;return n>-1&&n===a&&yl(t.matched[n],l.matched[a])&&jo(t.params,l.params)&&e(t.query)===e(l.query)&&t.hash===l.hash}function yl(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function jo(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const l in e)if(!U0(e[l],t[l]))return!1;return!0}function U0(e,t){return ut(e)?Mr(e,t):ut(t)?Mr(t,e):e===t}function Mr(e,t){return ut(t)?e.length===t.length&&e.every((l,n)=>l===t[n]):e.length===1&&e[0]===t}function G0(e,t){if(e.startsWith("/"))return e;if(!e)return t;const l=t.split("/"),n=e.split("/"),a=n[n.length-1];(a===".."||a===".")&&n.push("");let i=l.length-1,r,o;for(r=0;r1&&i--;else break;return l.slice(0,i).join("/")+"/"+n.slice(r-(r===n.length?1:0)).join("/")}var Jl;(function(e){e.pop="pop",e.push="push"})(Jl||(Jl={}));var Bl;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Bl||(Bl={}));function W0(e){if(!e)if(rl){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),z0(e)}const K0=/^[^#]+#/;function J0(e,t){return e.replace(K0,"#")+t}function Q0(e,t){const l=document.documentElement.getBoundingClientRect(),n=e.getBoundingClientRect();return{behavior:t.behavior,left:n.left-l.left-(t.left||0),top:n.top-l.top-(t.top||0)}}const ea=()=>({left:window.pageXOffset,top:window.pageYOffset});function Y0(e){let t;if("el"in e){const l=e.el,n=typeof l=="string"&&l.startsWith("#"),a=typeof l=="string"?n?document.getElementById(l.slice(1)):document.querySelector(l):l;if(!a)return;t=Q0(a,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.pageXOffset,t.top!=null?t.top:window.pageYOffset)}function Vr(e,t){return(history.state?history.state.position-t:-1)+e}const Ba=new Map;function X0(e,t){Ba.set(e,t)}function Z0(e){const t=Ba.get(e);return Ba.delete(e),t}let eh=()=>location.protocol+"//"+location.host;function Bo(e,t){const{pathname:l,search:n,hash:a}=t,i=e.indexOf("#");if(i>-1){let o=a.includes(e.slice(i))?e.slice(i).length:1,c=a.slice(o);return c[0]!=="/"&&(c="/"+c),$r(c,"")}return $r(l,e)+n+a}function th(e,t,l,n){let a=[],i=[],r=null;const o=({state:h})=>{const v=Bo(e,location),b=l.value,w=t.value;let x=0;if(h){if(l.value=v,t.value=h,r&&r===b){r=null;return}x=w?h.position-w.position:0}else n(v);a.forEach(g=>{g(l.value,b,{delta:x,type:Jl.pop,direction:x?x>0?Bl.forward:Bl.back:Bl.unknown})})};function c(){r=l.value}function u(h){a.push(h);const v=()=>{const b=a.indexOf(h);b>-1&&a.splice(b,1)};return i.push(v),v}function d(){const{history:h}=window;h.state&&h.replaceState(ge({},h.state,{scroll:ea()}),"")}function p(){for(const h of i)h();i=[],window.removeEventListener("popstate",o),window.removeEventListener("beforeunload",d)}return window.addEventListener("popstate",o),window.addEventListener("beforeunload",d,{passive:!0}),{pauseListeners:c,listen:u,destroy:p}}function Fr(e,t,l,n=!1,a=!1){return{back:e,current:t,forward:l,replaced:n,position:window.history.length,scroll:a?ea():null}}function lh(e){const{history:t,location:l}=window,n={value:Bo(e,l)},a={value:t.state};a.value||i(n.value,{back:null,current:n.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function i(c,u,d){const p=e.indexOf("#"),h=p>-1?(l.host&&document.querySelector("base")?e:e.slice(p))+c:eh()+e+c;try{t[d?"replaceState":"pushState"](u,"",h),a.value=u}catch(v){console.error(v),l[d?"replace":"assign"](h)}}function r(c,u){const d=ge({},t.state,Fr(a.value.back,c,a.value.forward,!0),u,{position:a.value.position});i(c,d,!0),n.value=c}function o(c,u){const d=ge({},a.value,t.state,{forward:c,scroll:ea()});i(d.current,d,!0);const p=ge({},Fr(n.value,c,null),{position:d.position+1},u);i(c,p,!1),n.value=c}return{location:n,state:a,push:o,replace:r}}function nh(e){e=W0(e);const t=lh(e),l=th(e,t.state,t.location,t.replace);function n(i,r=!0){r||l.pauseListeners(),history.go(i)}const a=ge({location:"",base:e,go:n,createHref:J0.bind(null,e)},t,l);return Object.defineProperty(a,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(a,"state",{enumerable:!0,get:()=>t.state.value}),a}function ah(e){return typeof e=="string"||e&&typeof e=="object"}function zo(e){return typeof e=="string"||typeof e=="symbol"}const Et={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},Ho=Symbol("");var Nr;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(Nr||(Nr={}));function bl(e,t){return ge(new Error,{type:e,[Ho]:!0},t)}function _t(e,t){return e instanceof Error&&Ho in e&&(t==null||!!(e.type&t))}const jr="[^/]+?",ih={sensitive:!1,strict:!1,start:!0,end:!0},rh=/[.+*?^${}()[\]/\\]/g;function sh(e,t){const l=ge({},ih,t),n=[];let a=l.start?"^":"";const i=[];for(const u of e){const d=u.length?[]:[90];l.strict&&!u.length&&(a+="/");for(let p=0;pt.length?t.length===1&&t[0]===40+40?1:-1:0}function ch(e,t){let l=0;const n=e.score,a=t.score;for(;l0&&t[t.length-1]<0}const uh={type:0,value:""},dh=/[a-zA-Z0-9_]/;function ph(e){if(!e)return[[]];if(e==="/")return[[uh]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(v){throw new Error(`ERR (${l})/"${u}": ${v}`)}let l=0,n=l;const a=[];let i;function r(){i&&a.push(i),i=[]}let o=0,c,u="",d="";function p(){u&&(l===0?i.push({type:0,value:u}):l===1||l===2||l===3?(i.length>1&&(c==="*"||c==="+")&&t(`A repeatable param (${u}) must be alone in its segment. eg: '/:ids+.`),i.push({type:1,value:u,regexp:d,repeatable:c==="*"||c==="+",optional:c==="*"||c==="?"})):t("Invalid state to consume buffer"),u="")}function h(){u+=c}for(;o{r(_)}:jl}function r(d){if(zo(d)){const p=n.get(d);p&&(n.delete(d),l.splice(l.indexOf(p),1),p.children.forEach(r),p.alias.forEach(r))}else{const p=l.indexOf(d);p>-1&&(l.splice(p,1),d.record.name&&n.delete(d.record.name),d.children.forEach(r),d.alias.forEach(r))}}function o(){return l}function c(d){let p=0;for(;p=0&&(d.record.path!==l[p].record.path||!qo(d,l[p]));)p++;l.splice(p,0,d),d.record.name&&!Hr(d)&&n.set(d.record.name,d)}function u(d,p){let h,v={},b,w;if("name"in d&&d.name){if(h=n.get(d.name),!h)throw bl(1,{location:d});w=h.record.name,v=ge(zr(p.params,h.keys.filter(_=>!_.optional).map(_=>_.name)),d.params&&zr(d.params,h.keys.map(_=>_.name))),b=h.stringify(v)}else if("path"in d)b=d.path,h=l.find(_=>_.re.test(b)),h&&(v=h.parse(b),w=h.record.name);else{if(h=p.name?n.get(p.name):l.find(_=>_.re.test(p.path)),!h)throw bl(1,{location:d,currentLocation:p});w=h.record.name,v=ge({},p.params,d.params),b=h.stringify(v)}const x=[];let g=h;for(;g;)x.unshift(g.record),g=g.parent;return{name:w,path:b,params:v,matched:x,meta:mh(x)}}return e.forEach(d=>i(d)),{addRoute:i,resolve:u,removeRoute:r,getRoutes:o,getRecordMatcher:a}}function zr(e,t){const l={};for(const n of t)n in e&&(l[n]=e[n]);return l}function fh(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:gh(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function gh(e){const t={},l=e.props||!1;if("component"in e)t.default=l;else for(const n in e.components)t[n]=typeof l=="object"?l[n]:l;return t}function Hr(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function mh(e){return e.reduce((t,l)=>ge(t,l.meta),{})}function qr(e,t){const l={};for(const n in e)l[n]=n in t?t[n]:e[n];return l}function qo(e,t){return t.children.some(l=>l===e||qo(e,l))}const Uo=/#/g,yh=/&/g,bh=/\//g,_h=/=/g,kh=/\?/g,Go=/\+/g,wh=/%5B/g,Eh=/%5D/g,Wo=/%5E/g,xh=/%60/g,Ko=/%7B/g,Lh=/%7C/g,Jo=/%7D/g,Th=/%20/g;function Ai(e){return encodeURI(""+e).replace(Lh,"|").replace(wh,"[").replace(Eh,"]")}function Ah(e){return Ai(e).replace(Ko,"{").replace(Jo,"}").replace(Wo,"^")}function za(e){return Ai(e).replace(Go,"%2B").replace(Th,"+").replace(Uo,"%23").replace(yh,"%26").replace(xh,"`").replace(Ko,"{").replace(Jo,"}").replace(Wo,"^")}function Ih(e){return za(e).replace(_h,"%3D")}function Ph(e){return Ai(e).replace(Uo,"%23").replace(kh,"%3F")}function Oh(e){return e==null?"":Ph(e).replace(bh,"%2F")}function zn(e){try{return decodeURIComponent(""+e)}catch{}return""+e}function Ch(e){const t={};if(e===""||e==="?")return t;const n=(e[0]==="?"?e.slice(1):e).split("&");for(let a=0;ai&&za(i)):[n&&za(n)]).forEach(i=>{i!==void 0&&(t+=(t.length?"&":"")+l,i!=null&&(t+="="+i))})}return t}function Rh(e){const t={};for(const l in e){const n=e[l];n!==void 0&&(t[l]=ut(n)?n.map(a=>a==null?null:""+a):n==null?n:""+n)}return t}const Sh=Symbol(""),Gr=Symbol(""),ta=Symbol(""),Ii=Symbol(""),Ha=Symbol("");function Cl(){let e=[];function t(n){return e.push(n),()=>{const a=e.indexOf(n);a>-1&&e.splice(a,1)}}function l(){e=[]}return{add:t,list:()=>e.slice(),reset:l}}function $t(e,t,l,n,a){const i=n&&(n.enterCallbacks[a]=n.enterCallbacks[a]||[]);return()=>new Promise((r,o)=>{const c=p=>{p===!1?o(bl(4,{from:l,to:t})):p instanceof Error?o(p):ah(p)?o(bl(2,{from:t,to:p})):(i&&n.enterCallbacks[a]===i&&typeof p=="function"&&i.push(p),r())},u=e.call(n&&n.instances[a],t,l,c);let d=Promise.resolve(u);e.length<3&&(d=d.then(c)),d.catch(p=>o(p))})}function ka(e,t,l,n){const a=[];for(const i of e)for(const r in i.components){let o=i.components[r];if(!(t!=="beforeRouteEnter"&&!i.instances[r]))if(Dh(o)){const u=(o.__vccOpts||o)[t];u&&a.push($t(u,l,n,i,r))}else{let c=o();a.push(()=>c.then(u=>{if(!u)return Promise.reject(new Error(`Couldn't resolve component "${r}" at "${i.path}"`));const d=j0(u)?u.default:u;i.components[r]=d;const h=(d.__vccOpts||d)[t];return h&&$t(h,l,n,i,r)()}))}}return a}function Dh(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function qa(e){const t=me(ta),l=me(Ii),n=E(()=>t.resolve(it(e.to))),a=E(()=>{const{matched:c}=n.value,{length:u}=c,d=c[u-1],p=l.matched;if(!d||!p.length)return-1;const h=p.findIndex(yl.bind(null,d));if(h>-1)return h;const v=Wr(c[u-2]);return u>1&&Wr(d)===v&&p[p.length-1].path!==v?p.findIndex(yl.bind(null,c[u-2])):h}),i=E(()=>a.value>-1&&Fh(l.params,n.value.params)),r=E(()=>a.value>-1&&a.value===l.matched.length-1&&jo(l.params,n.value.params));function o(c={}){return Vh(c)?t[it(e.replace)?"replace":"push"](it(e.to)).catch(jl):Promise.resolve()}return{route:n,href:E(()=>n.value.href),isActive:i,isExactActive:r,navigate:o}}const $h=M({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:qa,setup(e,{slots:t}){const l=tn(qa(e)),{options:n}=me(ta),a=E(()=>({[Kr(e.activeClass,n.linkActiveClass,"router-link-active")]:l.isActive,[Kr(e.exactActiveClass,n.linkExactActiveClass,"router-link-exact-active")]:l.isExactActive}));return()=>{const i=t.default&&t.default(l);return e.custom?i:s("a",{"aria-current":l.isExactActive?e.ariaCurrentValue:null,href:l.href,onClick:l.navigate,class:a.value},i)}}}),Mh=$h;function Vh(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function Fh(e,t){for(const l in t){const n=t[l],a=e[l];if(typeof n=="string"){if(n!==a)return!1}else if(!ut(a)||a.length!==n.length||n.some((i,r)=>i!==a[r]))return!1}return!0}function Wr(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const Kr=(e,t,l)=>e??t??l,Nh=M({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:l}){const n=me(Ha),a=E(()=>e.route||n.value),i=me(Gr,0),r=E(()=>{let u=it(i);const{matched:d}=a.value;let p;for(;(p=d[u])&&!p.components;)u++;return u}),o=E(()=>a.value.matched[r.value]);ot(Gr,E(()=>r.value+1)),ot(Sh,o),ot(Ha,a);const c=W();return ce(()=>[c.value,o.value,e.name],([u,d,p],[h,v,b])=>{d&&(d.instances[p]=u,v&&v!==d&&u&&u===h&&(d.leaveGuards.size||(d.leaveGuards=v.leaveGuards),d.updateGuards.size||(d.updateGuards=v.updateGuards))),u&&d&&(!v||!yl(d,v)||!h)&&(d.enterCallbacks[p]||[]).forEach(w=>w(u))},{flush:"post"}),()=>{const u=a.value,d=e.name,p=o.value,h=p&&p.components[d];if(!h)return Jr(l.default,{Component:h,route:u});const v=p.props[d],b=v?v===!0?u.params:typeof v=="function"?v(u):v:null,x=s(h,ge({},b,t,{onVnodeUnmounted:g=>{g.component.isUnmounted&&(p.instances[d]=null)},ref:c}));return Jr(l.default,{Component:x,route:u})||x}}});function Jr(e,t){if(!e)return null;const l=e(t);return l.length===1?l[0]:l}const Qo=Nh;function jh(e){const t=vh(e.routes,e),l=e.parseQuery||Ch,n=e.stringifyQuery||Ur,a=e.history,i=Cl(),r=Cl(),o=Cl(),c=Ue(Et);let u=Et;rl&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const d=ba.bind(null,A=>""+A),p=ba.bind(null,Oh),h=ba.bind(null,zn);function v(A,z){let N,K;return zo(A)?(N=t.getRecordMatcher(A),K=z):K=A,t.addRoute(K,N)}function b(A){const z=t.getRecordMatcher(A);z&&t.removeRoute(z)}function w(){return t.getRoutes().map(A=>A.record)}function x(A){return!!t.getRecordMatcher(A)}function g(A,z){if(z=ge({},z||c.value),typeof A=="string"){const k=_a(l,A,z.path),L=t.resolve({path:k.path},z),I=a.createHref(k.fullPath);return ge(k,L,{params:h(L.params),hash:zn(k.hash),redirectedFrom:void 0,href:I})}let N;if("path"in A)N=ge({},A,{path:_a(l,A.path,z.path).path});else{const k=ge({},A.params);for(const L in k)k[L]==null&&delete k[L];N=ge({},A,{params:p(k)}),z.params=p(z.params)}const K=t.resolve(N,z),fe=A.hash||"";K.params=d(h(K.params));const f=H0(n,ge({},A,{hash:Ah(fe),path:K.path})),m=a.createHref(f);return ge({fullPath:f,hash:fe,query:n===Ur?Rh(A.query):A.query||{}},K,{redirectedFrom:void 0,href:m})}function _(A){return typeof A=="string"?_a(l,A,c.value.path):ge({},A)}function P(A,z){if(u!==A)return bl(8,{from:z,to:A})}function C(A){return V(A)}function B(A){return C(ge(_(A),{replace:!0}))}function R(A){const z=A.matched[A.matched.length-1];if(z&&z.redirect){const{redirect:N}=z;let K=typeof N=="function"?N(A):N;return typeof K=="string"&&(K=K.includes("?")||K.includes("#")?K=_(K):{path:K},K.params={}),ge({query:A.query,hash:A.hash,params:"path"in K?{}:A.params},K)}}function V(A,z){const N=u=g(A),K=c.value,fe=A.state,f=A.force,m=A.replace===!0,k=R(N);if(k)return V(ge(_(k),{state:typeof k=="object"?ge({},fe,k.state):fe,force:f,replace:m}),z||N);const L=N;L.redirectedFrom=z;let I;return!f&&q0(n,K,N)&&(I=bl(16,{to:L,from:K}),pt(K,K,!0,!1)),(I?Promise.resolve(I):Y(L,K)).catch(S=>_t(S)?_t(S,2)?S:It(S):ve(S,L,K)).then(S=>{if(S){if(_t(S,2))return V(ge({replace:m},_(S.to),{state:typeof S.to=="object"?ge({},fe,S.to.state):fe,force:f}),z||L)}else S=H(L,K,!0,m,fe);return ne(L,K,S),S})}function T(A,z){const N=P(A,z);return N?Promise.reject(N):Promise.resolve()}function q(A){const z=ll.values().next().value;return z&&typeof z.runWithContext=="function"?z.runWithContext(A):A()}function Y(A,z){let N;const[K,fe,f]=Bh(A,z);N=ka(K.reverse(),"beforeRouteLeave",A,z);for(const k of K)k.leaveGuards.forEach(L=>{N.push($t(L,A,z))});const m=T.bind(null,A,z);return N.push(m),De(N).then(()=>{N=[];for(const k of i.list())N.push($t(k,A,z));return N.push(m),De(N)}).then(()=>{N=ka(fe,"beforeRouteUpdate",A,z);for(const k of fe)k.updateGuards.forEach(L=>{N.push($t(L,A,z))});return N.push(m),De(N)}).then(()=>{N=[];for(const k of f)if(k.beforeEnter)if(ut(k.beforeEnter))for(const L of k.beforeEnter)N.push($t(L,A,z));else N.push($t(k.beforeEnter,A,z));return N.push(m),De(N)}).then(()=>(A.matched.forEach(k=>k.enterCallbacks={}),N=ka(f,"beforeRouteEnter",A,z),N.push(m),De(N))).then(()=>{N=[];for(const k of r.list())N.push($t(k,A,z));return N.push(m),De(N)}).catch(k=>_t(k,8)?k:Promise.reject(k))}function ne(A,z,N){o.list().forEach(K=>q(()=>K(A,z,N)))}function H(A,z,N,K,fe){const f=P(A,z);if(f)return f;const m=z===Et,k=rl?history.state:{};N&&(K||m?a.replace(A.fullPath,ge({scroll:m&&k&&k.scroll},fe)):a.push(A.fullPath,fe)),c.value=A,pt(A,z,N,m),It()}let ee;function G(){ee||(ee=a.listen((A,z,N)=>{if(!mn.listening)return;const K=g(A),fe=R(K);if(fe){V(ge(fe,{replace:!0}),K).catch(jl);return}u=K;const f=c.value;rl&&X0(Vr(f.fullPath,N.delta),ea()),Y(K,f).catch(m=>_t(m,12)?m:_t(m,2)?(V(m.to,K).then(k=>{_t(k,20)&&!N.delta&&N.type===Jl.pop&&a.go(-1,!1)}).catch(jl),Promise.reject()):(N.delta&&a.go(-N.delta,!1),ve(m,K,f))).then(m=>{m=m||H(K,f,!1),m&&(N.delta&&!_t(m,8)?a.go(-N.delta,!1):N.type===Jl.pop&&_t(m,20)&&a.go(-1,!1)),ne(K,f,m)}).catch(jl)}))}let Se=Cl(),he=Cl(),_e;function ve(A,z,N){It(A);const K=he.list();return K.length?K.forEach(fe=>fe(A,z,N)):console.error(A),Promise.reject(A)}function bt(){return _e&&c.value!==Et?Promise.resolve():new Promise((A,z)=>{Se.add([A,z])})}function It(A){return _e||(_e=!A,G(),Se.list().forEach(([z,N])=>A?N(A):z()),Se.reset()),A}function pt(A,z,N,K){const{scrollBehavior:fe}=e;if(!rl||!fe)return Promise.resolve();const f=!N&&Z0(Vr(A.fullPath,0))||(K||!N)&&history.state&&history.state.scroll||null;return Tl().then(()=>fe(A,z,f)).then(m=>m&&Y0(m)).catch(m=>ve(m,A,z))}const je=A=>a.go(A);let tl;const ll=new Set,mn={currentRoute:c,listening:!0,addRoute:v,removeRoute:b,hasRoute:x,getRoutes:w,resolve:g,options:e,push:C,replace:B,go:je,back:()=>je(-1),forward:()=>je(1),beforeEach:i.add,beforeResolve:r.add,afterEach:o.add,onError:he.add,isReady:bt,install(A){const z=this;A.component("RouterLink",Mh),A.component("RouterView",Qo),A.config.globalProperties.$router=z,Object.defineProperty(A.config.globalProperties,"$route",{enumerable:!0,get:()=>it(c)}),rl&&!tl&&c.value===Et&&(tl=!0,C(a.location).catch(fe=>{}));const N={};for(const fe in Et)Object.defineProperty(N,fe,{get:()=>c.value[fe],enumerable:!0});A.provide(ta,z),A.provide(Ii,js(N)),A.provide(Ha,c);const K=A.unmount;ll.add(A),A.unmount=function(){ll.delete(A),ll.size<1&&(u=Et,ee&&ee(),ee=null,c.value=Et,tl=!1,_e=!1),K()}}};function De(A){return A.reduce((z,N)=>z.then(()=>q(N)),Promise.resolve())}return mn}function Bh(e,t){const l=[],n=[],a=[],i=Math.max(t.matched.length,e.matched.length);for(let r=0;ryl(u,o))?n.push(o):l.push(o));const c=e.matched[r];c&&(t.matched.find(u=>yl(u,c))||a.push(c))}return[l,n,a]}function Ve(){return me(ta)}function yt(){return me(Ii)}const de=({name:e="",color:t="currentColor"},{slots:l})=>{var n;return s("svg",{xmlns:"http://www.w3.org/2000/svg",class:["icon",`${e}-icon`],viewBox:"0 0 1024 1024",fill:t,"aria-label":`${e} icon`},(n=l.default)==null?void 0:n.call(l))};de.displayName="IconBase";const Yo=({size:e=48,stroke:t=4,wrapper:l=!0,height:n=2*e})=>{const a=s("svg",{xmlns:"http://www.w3.org/2000/svg",width:e,height:e,preserveAspectRatio:"xMidYMid",viewBox:"25 25 50 50"},[s("animateTransform",{attributeName:"transform",type:"rotate",dur:"2s",keyTimes:"0;1",repeatCount:"indefinite",values:"0;360"}),s("circle",{cx:"50",cy:"50",r:"20",fill:"none",stroke:"currentColor","stroke-width":t,"stroke-linecap":"round"},[s("animate",{attributeName:"stroke-dasharray",dur:"1.5s",keyTimes:"0;0.5;1",repeatCount:"indefinite",values:"1,200;90,200;1,200"}),s("animate",{attributeName:"stroke-dashoffset",dur:"1.5s",keyTimes:"0;0.5;1",repeatCount:"indefinite",values:"0;-35px;-125px"})])]);return l?s("div",{class:"loading-icon-wrapper",style:`display:flex;align-items:center;justify-content:center;height:${n}px`},a):a};Yo.displayName="LoadingIcon";const Xo=(e,{slots:t})=>{var l;return(l=t.default)==null?void 0:l.call(t)},Pi=(e="")=>{if(e){if(typeof e=="number")return new Date(e);const t=Date.parse(e.toString());if(!Number.isNaN(t))return new Date(t)}return null},la=(e,t)=>{let l=1;for(let n=0;n>6;return l+=l<<3,l^=l>>11,l%t},Zo=Array.isArray,zh=e=>typeof e=="function",Hh=e=>typeof e=="string";var qh=e=>e.startsWith("ftp://"),Oi=e=>/^(https?:)?\/\//.test(e),Uh=/.md((\?|#).*)?$/,Gh=(e,t="/")=>!!(Oi(e)||qh(e)||e.startsWith("/")&&!e.startsWith(t)&&!Uh.test(e)),ec=e=>Object.prototype.toString.call(e)==="[object Object]";function Wh(){const e=W(!1);return Xt()&&ke(()=>{e.value=!0}),e}function Kh(e){return Wh(),E(()=>!!e())}const Tt=e=>typeof e=="string",Ql=(e,t)=>Tt(e)&&e.startsWith(t),il=(e,t)=>Tt(e)&&e.endsWith(t),sn=Object.entries,Jh=Object.fromEntries,gt=Object.keys,Qh=e=>(e.endsWith(".md")&&(e=`${e.slice(0,-3)}.html`),!e.endsWith("/")&&!e.endsWith(".html")&&(e=`${e}.html`),e=e.replace(/(^|\/)(?:README|index).html$/i,"$1"),e),tc=e=>{const[t,l=""]=e.split("#");return t?`${Qh(t)}${l?`#${l}`:""}`:e},Qr=e=>ec(e)&&Tt(e.name),Yl=(e,t=!1)=>e?Zo(e)?e.map(l=>Tt(l)?{name:l}:Qr(l)?l:null).filter(l=>l!==null):Tt(e)?[{name:e}]:Qr(e)?[e]:(console.error(`Expect "author" to be \`AuthorInfo[] | AuthorInfo | string[] | string ${t?"":"| false"} | undefined\`, but got`,e),[]):[],lc=(e,t)=>{if(e){if(Zo(e)&&e.every(Tt))return e;if(Tt(e))return[e];console.error(`Expect ${t||"value"} to be \`string[] | string | undefined\`, but got`,e)}return[]},nc=e=>lc(e,"category"),ac=e=>lc(e,"tag"),na=e=>Ql(e,"/");let Yh=class{constructor(){oa(this,"containerElement");oa(this,"messageElements",{});const t="message-container",l=document.getElementById(t);l?this.containerElement=l:(this.containerElement=document.createElement("div"),this.containerElement.id=t,document.body.appendChild(this.containerElement))}pop(t,l=2e3){const n=document.createElement("div"),a=Date.now();return n.className="message move-in",n.innerHTML=t,this.containerElement.appendChild(n),this.messageElements[a]=n,l>0&&setTimeout(()=>{this.close(a)},l),a}close(t){if(t){const l=this.messageElements[t];l.classList.remove("move-in"),l.classList.add("move-out"),l.addEventListener("animationend",()=>{l.remove(),delete this.messageElements[t]})}else gt(this.messageElements).forEach(l=>this.close(Number(l)))}destroy(){document.body.removeChild(this.containerElement)}};const ic=/#.*$/u,Xh=e=>{const t=ic.exec(e);return t?t[0]:""},Yr=e=>decodeURI(e).replace(ic,"").replace(/(index)?\.(md|html)$/,""),Ci=(e,t)=>{if(t===void 0)return!1;const l=Yr(e.path),n=Yr(t),a=Xh(t);return a?a===e.hash&&(!n||l===n):l===n},Zh=e=>Oi(e)?e:`https://github.com/${e}`,rc=e=>!Oi(e)||/github\.com/.test(e)?"GitHub":/bitbucket\.org/.test(e)?"Bitbucket":/gitlab\.com/.test(e)?"GitLab":/gitee\.com/.test(e)?"Gitee":null,_l=(e,...t)=>{const l=e.resolve(...t),n=l.matched[l.matched.length-1];if(!(n!=null&&n.redirect))return l;const{redirect:a}=n,i=zh(a)?a(l):a,r=Hh(i)?{path:i}:i;return _l(e,{hash:l.hash,query:l.query,params:l.params,...r})},e1=e=>{if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget){const t=e.currentTarget.getAttribute("target");if(t!=null&&t.match(/\b_blank\b/i))return}return e.preventDefault(),!0}},Re=({to:e=""},{slots:t})=>{var l;const n=Ve(),a=(i={})=>e1(i)?n.push(e).catch():Promise.resolve();return s("a",{class:"vp-link",href:Le(tc(e)),onClick:a},(l=t.default)==null?void 0:l.call(t))};Re.displayName="VPLink";const sc=()=>s(de,{name:"github"},()=>s("path",{d:"M511.957 21.333C241.024 21.333 21.333 240.981 21.333 512c0 216.832 140.544 400.725 335.574 465.664 24.49 4.395 32.256-10.07 32.256-23.083 0-11.69.256-44.245 0-85.205-136.448 29.61-164.736-64.64-164.736-64.64-22.315-56.704-54.4-71.765-54.4-71.765-44.587-30.464 3.285-29.824 3.285-29.824 49.195 3.413 75.179 50.517 75.179 50.517 43.776 75.008 114.816 53.333 142.762 40.79 4.523-31.66 17.152-53.377 31.19-65.537-108.971-12.458-223.488-54.485-223.488-242.602 0-53.547 19.114-97.323 50.517-131.67-5.035-12.33-21.93-62.293 4.779-129.834 0 0 41.258-13.184 134.912 50.346a469.803 469.803 0 0 1 122.88-16.554c41.642.213 83.626 5.632 122.88 16.554 93.653-63.488 134.784-50.346 134.784-50.346 26.752 67.541 9.898 117.504 4.864 129.834 31.402 34.347 50.474 78.123 50.474 131.67 0 188.586-114.73 230.016-224.042 242.09 17.578 15.232 33.578 44.672 33.578 90.454v135.85c0 13.142 7.936 27.606 32.854 22.87C862.25 912.597 1002.667 728.747 1002.667 512c0-271.019-219.648-490.667-490.71-490.667z"}));sc.displayName="GitHubIcon";const oc=()=>s(de,{name:"gitlab"},()=>s("path",{d:"M229.333 78.688C223.52 62 199.895 62 193.895 78.688L87.958 406.438h247.5c-.188 0-106.125-327.75-106.125-327.75zM33.77 571.438c-4.875 15 .563 31.687 13.313 41.25l464.812 345L87.77 406.438zm301.5-165 176.813 551.25 176.812-551.25zm655.125 165-54-165-424.312 551.25 464.812-345c12.938-9.563 18.188-26.25 13.5-41.25zM830.27 78.688c-5.812-16.688-29.437-16.688-35.437 0l-106.125 327.75h247.5z"}));oc.displayName="GitLabIcon";const cc=()=>s(de,{name:"gitee"},()=>s("path",{d:"M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm242.97-533.34H482.39a23.7 23.7 0 0 0-23.7 23.7l-.03 59.28c0 13.08 10.59 23.7 23.7 23.7h165.96a23.7 23.7 0 0 1 23.7 23.7v11.85a71.1 71.1 0 0 1-71.1 71.1H375.71a23.7 23.7 0 0 1-23.7-23.7V423.11a71.1 71.1 0 0 1 71.1-71.1h331.8a23.7 23.7 0 0 0 23.7-23.7l.06-59.25a23.73 23.73 0 0 0-23.7-23.73H423.11a177.78 177.78 0 0 0-177.78 177.75v331.83c0 13.08 10.62 23.7 23.7 23.7h349.62a159.99 159.99 0 0 0 159.99-159.99V482.33a23.7 23.7 0 0 0-23.7-23.7z"}));cc.displayName="GiteeIcon";const uc=()=>s(de,{name:"bitbucket"},()=>s("path",{d:"M575.256 490.862c6.29 47.981-52.005 85.723-92.563 61.147-45.714-20.004-45.714-92.562-1.133-113.152 38.29-23.442 93.696 7.424 93.696 52.005zm63.451-11.996c-10.276-81.152-102.29-134.839-177.152-101.156-47.433 21.138-79.433 71.424-77.129 124.562 2.853 69.705 69.157 126.866 138.862 120.576S647.3 548.571 638.708 478.83zm136.558-309.723c-25.161-33.134-67.986-38.839-105.728-45.13-106.862-17.151-216.576-17.7-323.438 1.134-35.438 5.706-75.447 11.996-97.719 43.996 36.572 34.304 88.576 39.424 135.424 45.129 84.553 10.862 171.447 11.447 256 .585 47.433-5.705 99.987-10.276 135.424-45.714zm32.585 591.433c-16.018 55.99-6.839 131.438-66.304 163.986-102.29 56.576-226.304 62.867-338.87 42.862-59.43-10.862-129.135-29.696-161.72-85.723-14.3-54.858-23.442-110.848-32.585-166.84l3.438-9.142 10.276-5.157c170.277 112.567 408.576 112.567 579.438 0 26.844 8.01 6.84 40.558 6.29 60.014zm103.424-549.157c-19.42 125.148-41.728 249.71-63.415 374.272-6.29 36.572-41.728 57.162-71.424 72.558-106.862 53.724-231.424 62.866-348.562 50.286-79.433-8.558-160.585-29.696-225.134-79.433-30.28-23.443-30.28-63.415-35.986-97.134-20.005-117.138-42.862-234.277-57.161-352.585 6.839-51.42 64.585-73.728 107.447-89.71 57.16-21.138 118.272-30.866 178.87-36.571 129.134-12.58 261.157-8.01 386.304 28.562 44.581 13.13 92.563 31.415 122.844 69.705 13.714 17.7 9.143 40.01 6.29 60.014z"}));uc.displayName="BitbucketIcon";const dc=()=>s(de,{name:"source"},()=>s("path",{d:"M601.92 475.2c0 76.428-8.91 83.754-28.512 99.594-14.652 11.88-43.956 14.058-78.012 16.434-18.81 1.386-40.392 2.97-62.172 6.534-18.612 2.97-36.432 9.306-53.064 17.424V299.772c37.818-21.978 63.36-62.766 63.36-109.692 0-69.894-56.826-126.72-126.72-126.72S190.08 120.186 190.08 190.08c0 46.926 25.542 87.714 63.36 109.692v414.216c-37.818 21.978-63.36 62.766-63.36 109.692 0 69.894 56.826 126.72 126.72 126.72s126.72-56.826 126.72-126.72c0-31.086-11.286-59.598-29.7-81.576 13.266-9.504 27.522-17.226 39.996-19.206 16.038-2.574 32.868-3.762 50.688-5.148 48.312-3.366 103.158-7.326 148.896-44.55 61.182-49.698 74.25-103.158 75.24-187.902V475.2h-126.72zM316.8 126.72c34.848 0 63.36 28.512 63.36 63.36s-28.512 63.36-63.36 63.36-63.36-28.512-63.36-63.36 28.512-63.36 63.36-63.36zm0 760.32c-34.848 0-63.36-28.512-63.36-63.36s28.512-63.36 63.36-63.36 63.36 28.512 63.36 63.36-28.512 63.36-63.36 63.36zM823.68 158.4h-95.04V63.36h-126.72v95.04h-95.04v126.72h95.04v95.04h126.72v-95.04h95.04z"}));dc.displayName="SourceIcon";const ct=(e,t)=>{const l=t?t._instance:Xt();return ec(l==null?void 0:l.appContext.components)&&(e in l.appContext.components||tt(e)in l.appContext.components||en(tt(e))in l.appContext.components)},t1=()=>Kh(()=>typeof window<"u"&&window.navigator&&"userAgent"in window.navigator),l1=()=>{const e=t1();return E(()=>e.value&&/\b(?:Android|iPhone)/i.test(navigator.userAgent))},on=e=>{const t=mt();return E(()=>e[t.value])};var n1=Object.defineProperty,a1=Object.defineProperties,i1=Object.getOwnPropertyDescriptors,Xr=Object.getOwnPropertySymbols,r1=Object.prototype.hasOwnProperty,s1=Object.prototype.propertyIsEnumerable,Zr=(e,t,l)=>t in e?n1(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l,o1=(e,t)=>{for(var l in t||(t={}))r1.call(t,l)&&Zr(e,l,t[l]);if(Xr)for(var l of Xr(t))s1.call(t,l)&&Zr(e,l,t[l]);return e},c1=(e,t)=>a1(e,i1(t));function es(e,t){var l;const n=Ue();return Xs(()=>{n.value=e()},c1(o1({},t),{flush:(l=t==null?void 0:t.flush)!=null?l:"sync"})),Yt(n)}function Al(e){return Ps()?(td(e),!0):!1}function ze(e){return typeof e=="function"?e():it(e)}const cn=typeof window<"u",Xl=()=>{},ts=u1();function u1(){var e;return cn&&((e=window==null?void 0:window.navigator)==null?void 0:e.userAgent)&&/iP(ad|hone|od)/.test(window.navigator.userAgent)}function pc(e,t){function l(...n){return new Promise((a,i)=>{Promise.resolve(e(()=>t.apply(this,n),{fn:t,thisArg:this,args:n})).then(a).catch(i)})}return l}const hc=e=>e();function d1(e,t=!0,l=!0,n=!1){let a=0,i,r=!0,o=Xl,c;const u=()=>{i&&(clearTimeout(i),i=void 0,o(),o=Xl)};return p=>{const h=ze(e),v=Date.now()-a,b=()=>c=p();return u(),h<=0?(a=Date.now(),b()):(v>h&&(l||!r)?(a=Date.now(),b()):t&&(c=new Promise((w,x)=>{o=n?x:w,i=setTimeout(()=>{a=Date.now(),r=!0,w(b()),u()},Math.max(0,h-v))})),!l&&!i&&(i=setTimeout(()=>r=!0,h)),r=!1,c)}}function p1(e=hc){const t=W(!0);function l(){t.value=!1}function n(){t.value=!0}const a=(...i)=>{t.value&&e(...i)};return{isActive:Yt(t),pause:l,resume:n,eventFilter:a}}function h1(...e){if(e.length!==1)return Ll(...e);const t=e[0];return typeof t=="function"?Yt(Rd(()=>({get:t,set:Xl}))):W(t)}function v1(e,t=200,l=!1,n=!0,a=!1){return pc(d1(t,l,n,a),e)}function vc(e,t=!0){Xt()?ke(e):t?e():Tl(e)}function f1(e){Xt()&&an(e)}function g1(e,t,l={}){const{immediate:n=!0}=l,a=W(!1);let i=null;function r(){i&&(clearTimeout(i),i=null)}function o(){a.value=!1,r()}function c(...u){r(),a.value=!0,i=setTimeout(()=>{a.value=!1,i=null,e(...u)},ze(t))}return n&&(a.value=!0,cn&&c()),Al(o),{isPending:Yt(a),start:c,stop:o}}function ls(e=!1,t={}){const{truthyValue:l=!0,falsyValue:n=!1}=t,a=Oe(e),i=W(e);function r(o){if(arguments.length)return i.value=o,i.value;{const c=ze(l);return i.value=i.value===c?ze(n):c,i.value}}return a?r:[i,r]}var ns=Object.getOwnPropertySymbols,m1=Object.prototype.hasOwnProperty,y1=Object.prototype.propertyIsEnumerable,b1=(e,t)=>{var l={};for(var n in e)m1.call(e,n)&&t.indexOf(n)<0&&(l[n]=e[n]);if(e!=null&&ns)for(var n of ns(e))t.indexOf(n)<0&&y1.call(e,n)&&(l[n]=e[n]);return l};function _1(e,t,l={}){const n=l,{eventFilter:a=hc}=n,i=b1(n,["eventFilter"]);return ce(e,pc(a,t),i)}var k1=Object.defineProperty,w1=Object.defineProperties,E1=Object.getOwnPropertyDescriptors,Hn=Object.getOwnPropertySymbols,fc=Object.prototype.hasOwnProperty,gc=Object.prototype.propertyIsEnumerable,as=(e,t,l)=>t in e?k1(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l,x1=(e,t)=>{for(var l in t||(t={}))fc.call(t,l)&&as(e,l,t[l]);if(Hn)for(var l of Hn(t))gc.call(t,l)&&as(e,l,t[l]);return e},L1=(e,t)=>w1(e,E1(t)),T1=(e,t)=>{var l={};for(var n in e)fc.call(e,n)&&t.indexOf(n)<0&&(l[n]=e[n]);if(e!=null&&Hn)for(var n of Hn(e))t.indexOf(n)<0&&gc.call(e,n)&&(l[n]=e[n]);return l};function A1(e,t,l={}){const n=l,{eventFilter:a}=n,i=T1(n,["eventFilter"]),{eventFilter:r,pause:o,resume:c,isActive:u}=p1(a);return{stop:_1(e,t,L1(x1({},i),{eventFilter:r})),pause:o,resume:c,isActive:u}}function Ft(e){var t;const l=ze(e);return(t=l==null?void 0:l.$el)!=null?t:l}const zt=cn?window:void 0,mc=cn?window.document:void 0,I1=cn?window.navigator:void 0;function Me(...e){let t,l,n,a;if(typeof e[0]=="string"||Array.isArray(e[0])?([l,n,a]=e,t=zt):[t,l,n,a]=e,!t)return Xl;Array.isArray(l)||(l=[l]),Array.isArray(n)||(n=[n]);const i=[],r=()=>{i.forEach(d=>d()),i.length=0},o=(d,p,h,v)=>(d.addEventListener(p,h,v),()=>d.removeEventListener(p,h,v)),c=ce(()=>[Ft(t),ze(a)],([d,p])=>{r(),d&&i.push(...l.flatMap(h=>n.map(v=>o(d,h,v,p))))},{immediate:!0,flush:"post"}),u=()=>{c(),r()};return Al(u),u}function P1(){const e=W(!1);return Xt()&&ke(()=>{e.value=!0}),e}function aa(e){const t=P1();return E(()=>(t.value,!!e()))}function yc(e,t={}){const{window:l=zt}=t,n=aa(()=>l&&"matchMedia"in l&&typeof l.matchMedia=="function");let a;const i=W(!1),r=u=>{i.value=u.matches},o=()=>{a&&("removeEventListener"in a?a.removeEventListener("change",r):a.removeListener(r))},c=Xs(()=>{n.value&&(o(),a=l.matchMedia(ze(e)),"addEventListener"in a?a.addEventListener("change",r):a.addListener(r),i.value=a.matches)});return Al(()=>{c(),o(),a=void 0}),i}function O1(e={}){const{navigator:t=I1,read:l=!1,source:n,copiedDuring:a=1500,legacy:i=!1}=e,r=aa(()=>t&&"clipboard"in t),o=E(()=>r.value||i),c=W(""),u=W(!1),d=g1(()=>u.value=!1,a);function p(){r.value?t.clipboard.readText().then(w=>{c.value=w}):c.value=b()}o.value&&l&&Me(["copy","cut"],p);async function h(w=ze(n)){o.value&&w!=null&&(r.value?await t.clipboard.writeText(w):v(w),c.value=w,u.value=!0,d.start())}function v(w){const x=document.createElement("textarea");x.value=w??"",x.style.position="absolute",x.style.opacity="0",document.body.appendChild(x),x.select(),document.execCommand("copy"),x.remove()}function b(){var w,x,g;return(g=(x=(w=document==null?void 0:document.getSelection)==null?void 0:w.call(document))==null?void 0:x.toString())!=null?g:""}return{isSupported:o,text:c,copied:u,copy:h}}const Tn=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},An="__vueuse_ssr_handlers__",C1=R1();function R1(){return An in Tn||(Tn[An]=Tn[An]||{}),Tn[An]}function S1(e,t){return C1[e]||t}function D1(e){return e==null?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":typeof e=="boolean"?"boolean":typeof e=="string"?"string":typeof e=="object"?"object":Number.isNaN(e)?"any":"number"}var $1=Object.defineProperty,is=Object.getOwnPropertySymbols,M1=Object.prototype.hasOwnProperty,V1=Object.prototype.propertyIsEnumerable,rs=(e,t,l)=>t in e?$1(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l,ss=(e,t)=>{for(var l in t||(t={}))M1.call(t,l)&&rs(e,l,t[l]);if(is)for(var l of is(t))V1.call(t,l)&&rs(e,l,t[l]);return e};const F1={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}},os="vueuse-storage";function Ri(e,t,l,n={}){var a;const{flush:i="pre",deep:r=!0,listenToStorageChanges:o=!0,writeDefaults:c=!0,mergeDefaults:u=!1,shallow:d,window:p=zt,eventFilter:h,onError:v=T=>{console.error(T)}}=n,b=(d?Ue:W)(t);if(!l)try{l=S1("getDefaultStorage",()=>{var T;return(T=zt)==null?void 0:T.localStorage})()}catch(T){v(T)}if(!l)return b;const w=ze(t),x=D1(w),g=(a=n.serializer)!=null?a:F1[x],{pause:_,resume:P}=A1(b,()=>C(b.value),{flush:i,deep:r,eventFilter:h});return p&&o&&(Me(p,"storage",V),Me(p,os,R)),V(),b;function C(T){try{if(T==null)l.removeItem(e);else{const q=g.write(T),Y=l.getItem(e);Y!==q&&(l.setItem(e,q),p&&p.dispatchEvent(new CustomEvent(os,{detail:{key:e,oldValue:Y,newValue:q,storageArea:l}})))}}catch(q){v(q)}}function B(T){const q=T?T.newValue:l.getItem(e);if(q==null)return c&&w!==null&&l.setItem(e,g.write(w)),w;if(!T&&u){const Y=g.read(q);return typeof u=="function"?u(Y,w):x==="object"&&!Array.isArray(Y)?ss(ss({},w),Y):Y}else return typeof q!="string"?q:g.read(q)}function R(T){V(T.detail)}function V(T){if(!(T&&T.storageArea!==l)){if(T&&T.key==null){b.value=w;return}if(!(T&&T.key!==e)){_();try{b.value=B(T)}catch(q){v(q)}finally{T?Tl(P):P()}}}}}function N1(e){return yc("(prefers-color-scheme: dark)",e)}var cs=Object.getOwnPropertySymbols,j1=Object.prototype.hasOwnProperty,B1=Object.prototype.propertyIsEnumerable,z1=(e,t)=>{var l={};for(var n in e)j1.call(e,n)&&t.indexOf(n)<0&&(l[n]=e[n]);if(e!=null&&cs)for(var n of cs(e))t.indexOf(n)<0&&B1.call(e,n)&&(l[n]=e[n]);return l};function H1(e,t,l={}){const n=l,{window:a=zt}=n,i=z1(n,["window"]);let r;const o=aa(()=>a&&"ResizeObserver"in a),c=()=>{r&&(r.disconnect(),r=void 0)},u=E(()=>Array.isArray(e)?e.map(h=>Ft(h)):[Ft(e)]),d=ce(u,h=>{if(c(),o.value&&a){r=new ResizeObserver(t);for(const v of h)v&&r.observe(v,i)}},{immediate:!0,flush:"post",deep:!0}),p=()=>{c(),d()};return Al(p),{isSupported:o,stop:p}}function q1(e,t={width:0,height:0},l={}){const{window:n=zt,box:a="content-box"}=l,i=E(()=>{var c,u;return(u=(c=Ft(e))==null?void 0:c.namespaceURI)==null?void 0:u.includes("svg")}),r=W(t.width),o=W(t.height);return H1(e,([c])=>{const u=a==="border-box"?c.borderBoxSize:a==="content-box"?c.contentBoxSize:c.devicePixelContentBoxSize;if(n&&i.value){const d=Ft(e);if(d){const p=n.getComputedStyle(d);r.value=Number.parseFloat(p.width),o.value=Number.parseFloat(p.height)}}else if(u){const d=Array.isArray(u)?u:[u];r.value=d.reduce((p,{inlineSize:h})=>p+h,0),o.value=d.reduce((p,{blockSize:h})=>p+h,0)}else r.value=c.contentRect.width,o.value=c.contentRect.height},l),ce(()=>Ft(e),c=>{r.value=c?t.width:0,o.value=c?t.height:0}),{width:r,height:o}}const us=["fullscreenchange","webkitfullscreenchange","webkitendfullscreen","mozfullscreenchange","MSFullscreenChange"];function Si(e,t={}){const{document:l=mc,autoExit:n=!1}=t,a=E(()=>{var g;return(g=Ft(e))!=null?g:l==null?void 0:l.querySelector("html")}),i=W(!1),r=E(()=>["requestFullscreen","webkitRequestFullscreen","webkitEnterFullscreen","webkitEnterFullScreen","webkitRequestFullScreen","mozRequestFullScreen","msRequestFullscreen"].find(g=>l&&g in l||a.value&&g in a.value)),o=E(()=>["exitFullscreen","webkitExitFullscreen","webkitExitFullScreen","webkitCancelFullScreen","mozCancelFullScreen","msExitFullscreen"].find(g=>l&&g in l||a.value&&g in a.value)),c=E(()=>["fullScreen","webkitIsFullScreen","webkitDisplayingFullscreen","mozFullScreen","msFullscreenElement"].find(g=>l&&g in l||a.value&&g in a.value)),u=["fullscreenElement","webkitFullscreenElement","mozFullScreenElement","msFullscreenElement"].find(g=>l&&g in l),d=aa(()=>a.value&&l&&r.value!==void 0&&o.value!==void 0&&c.value!==void 0),p=()=>u?(l==null?void 0:l[u])===a.value:!1,h=()=>{if(c.value){if(l&&l[c.value]!=null)return l[c.value];{const g=a.value;if((g==null?void 0:g[c.value])!=null)return!!g[c.value]}}return!1};async function v(){if(!(!d.value||!i.value)){if(o.value)if((l==null?void 0:l[o.value])!=null)await l[o.value]();else{const g=a.value;(g==null?void 0:g[o.value])!=null&&await g[o.value]()}i.value=!1}}async function b(){if(!d.value||i.value)return;h()&&await v();const g=a.value;r.value&&(g==null?void 0:g[r.value])!=null&&(await g[r.value](),i.value=!0)}async function w(){await(i.value?v():b())}const x=()=>{const g=h();(!g||g&&p())&&(i.value=g)};return Me(l,us,x,!1),Me(()=>Ft(a),us,x,!1),n&&Al(v),{isSupported:d,isFullscreen:i,enter:b,exit:v,toggle:w}}function wa(e,t=Xl,l={}){const{immediate:n=!0,manual:a=!1,type:i="text/javascript",async:r=!0,crossOrigin:o,referrerPolicy:c,noModule:u,defer:d,document:p=mc,attrs:h={}}=l,v=W(null);let b=null;const w=_=>new Promise((P,C)=>{const B=T=>(v.value=T,P(T),T);if(!p){P(!1);return}let R=!1,V=p.querySelector(`script[src="${ze(e)}"]`);V?V.hasAttribute("data-loaded")&&B(V):(V=p.createElement("script"),V.type=i,V.async=r,V.src=ze(e),d&&(V.defer=d),o&&(V.crossOrigin=o),u&&(V.noModule=u),c&&(V.referrerPolicy=c),Object.entries(h).forEach(([T,q])=>V==null?void 0:V.setAttribute(T,q)),R=!0),V.addEventListener("error",T=>C(T)),V.addEventListener("abort",T=>C(T)),V.addEventListener("load",()=>{V.setAttribute("data-loaded","true"),t(V),B(V)}),R&&(V=p.head.appendChild(V)),_||B(V)}),x=(_=!0)=>(b||(b=w(_)),b),g=()=>{if(!p)return;b=null,v.value&&(v.value=null);const _=p.querySelector(`script[src="${ze(e)}"]`);_&&p.head.removeChild(_)};return n&&!a&&vc(x),a||f1(g),{scriptTag:v,load:x,unload:g}}function bc(e){const t=window.getComputedStyle(e);if(t.overflowX==="scroll"||t.overflowY==="scroll"||t.overflowX==="auto"&&e.clientWidth1?!0:(t.preventDefault&&t.preventDefault(),!1)}function _c(e,t=!1){const l=W(t);let n=null,a;ce(h1(e),o=>{if(o){const c=o;a=c.style.overflow,l.value&&(c.style.overflow="hidden")}},{immediate:!0});const i=()=>{const o=ze(e);!o||l.value||(ts&&(n=Me(o,"touchmove",c=>{U1(c)},{passive:!1})),o.style.overflow="hidden",l.value=!0)},r=()=>{const o=ze(e);!o||!l.value||(ts&&(n==null||n()),o.style.overflow=a,l.value=!1)};return Al(r),E({get(){return l.value},set(o){o?i():r()}})}function G1({window:e=zt}={}){if(!e)return{x:W(0),y:W(0)};const t=W(e.scrollX),l=W(e.scrollY);return Me(e,"scroll",()=>{t.value=e.scrollX,l.value=e.scrollY},{capture:!1,passive:!0}),{x:t,y:l}}function W1(e={}){const{window:t=zt,initialWidth:l=Number.POSITIVE_INFINITY,initialHeight:n=Number.POSITIVE_INFINITY,listenOrientation:a=!0,includeScrollbar:i=!0}=e,r=W(l),o=W(n),c=()=>{t&&(i?(r.value=t.innerWidth,o.value=t.innerHeight):(r.value=t.document.documentElement.clientWidth,o.value=t.document.documentElement.clientHeight))};if(c(),vc(c),Me("resize",c,{passive:!0}),a){const u=yc("(orientation: portrait)");ce(u,()=>c())}return{width:r,height:o}}var K1=M({name:"FontIcon",props:{icon:{type:String,default:""},color:{type:String,default:""},size:{type:[String,Number],default:""}},setup(e){const t=E(()=>{const n=["font-icon icon"],a=`fas fa-${e.icon}`;return n.push("fa-fw fa-sm"),n.push(e.icon.includes(" ")?e.icon:a),n}),l=E(()=>{const n={};return e.color&&(n.color=e.color),e.size&&(n["font-size"]=Number.isNaN(Number(e.size))?e.size:`${e.size}px`),gt(n).length?n:null});return()=>e.icon?s("span",{key:e.icon,class:t.value,style:l.value}):null}});const J1="accelerometer; autoplay; clipboard-write; encrypted-media; fullscreen; gyroscope; picture-in-picture",ds=e=>ae(e)?e:`${e}px`,Q1=(e,t=0)=>{const l=Ue(),n=E(()=>ds(it(e.width)||"100%")),a=W("auto"),i=c=>{if(ae(c)){const[u,d]=c.split(":"),p=Number(u)/Number(d);if(!Number.isNaN(p))return p}return typeof c=="number"?c:16/9},r=c=>{const u=it(e.height),d=i(it(e.ratio));return u?ds(u):`${Number(c)/d+it(t)}px`},o=()=>{l.value&&(a.value=r(l.value.clientWidth))};return ke(()=>{o(),Oe(t)&&ce(t,()=>o()),Me("orientationchange",()=>o()),Me("resize",()=>o())}),{el:l,width:n,height:a}},ps="https://player.bilibili.com/player.html";var Y1=M({name:"BiliBili",props:{bvid:{type:String,default:""},aid:{type:String,default:""},cid:{type:String,default:""},title:{type:String,default:"A BiliBili video"},page:{type:[String,Number],default:1},width:{type:[String,Number],default:"100%"},height:{type:[String,Number],default:void 0},ratio:{type:[String,Number],default:16/9},time:{type:[String,Number],default:0},autoplay:Boolean},setup(e){const{el:t,width:l,height:n}=Q1(e),a=W(!1),i=E(()=>{const{aid:r,bvid:o,cid:c,autoplay:u,time:d,page:p}=e;return r&&c?`${ps}?aid=${r}&cid=${c}&t=${d}&autoplay=${u?1:0}&page=${p}`:o?`${ps}?bvid=${o}&t=${d}&autoplay=${u?1:0}`:null});return()=>i.value?[s("div",{class:"bilibili-desc"},s("a",{class:"sr-only",href:i.value},e.title)),s("iframe",{ref:t,src:i.value,title:e.title,class:"bilibili-iframe",allow:J1,style:{width:l.value,height:a.value?n.value:0},onLoad:()=>{a.value=!0}}),a.value?null:s(Yo)]:[]}});const kc=()=>s(de,{name:"back-to-top"},()=>[s("path",{d:"M512 843.2c-36.2 0-66.4-13.6-85.8-21.8-10.8-4.6-22.6 3.6-21.8 15.2l7 102c.4 6.2 7.6 9.4 12.6 5.6l29-22c3.6-2.8 9-1.8 11.4 2l41 64.2c3 4.8 10.2 4.8 13.2 0l41-64.2c2.4-3.8 7.8-4.8 11.4-2l29 22c5 3.8 12.2.6 12.6-5.6l7-102c.8-11.6-11-20-21.8-15.2-19.6 8.2-49.6 21.8-85.8 21.8z"}),s("path",{d:"m795.4 586.2-96-98.2C699.4 172 513 32 513 32S324.8 172 324.8 488l-96 98.2c-3.6 3.6-5.2 9-4.4 14.2L261.2 824c1.8 11.4 14.2 17 23.6 10.8L419 744s41.4 40 94.2 40c52.8 0 92.2-40 92.2-40l134.2 90.8c9.2 6.2 21.6.6 23.6-10.8l37-223.8c.4-5.2-1.2-10.4-4.8-14zM513 384c-34 0-61.4-28.6-61.4-64s27.6-64 61.4-64c34 0 61.4 28.6 61.4 64S547 384 513 384z"})]);kc.displayName="BackToTopIcon";var X1=M({name:"BackToTop",props:{threshold:{type:Number,default:100},noProgress:Boolean},setup(e){const t=Ee(),l=on({"/":{backToTop:"返回顶部"}}),n=Ue(),{height:a}=q1(n),{height:i}=W1(),{y:r}=G1(),o=E(()=>t.value.backToTop!==!1&&r.value>e.threshold),c=E(()=>r.value/(a.value-i.value));return ke(()=>{n.value=document.body}),()=>s(Bt,{name:"fade"},()=>o.value?s("button",{type:"button",class:"vp-back-to-top-button","aria-label":l.value.backToTop,"data-balloon-pos":"left",onClick:()=>{window.scrollTo({top:0,behavior:"smooth"})}},[e.noProgress?null:s("svg",{class:"vp-scroll-progress"},s("circle",{cx:"50%",cy:"50%",style:{"stroke-dasharray":`calc(${Math.PI*c.value*100}% - ${4*Math.PI}px) calc(${Math.PI*100}% - ${4*Math.PI}px)`}})),s(kc)]):null)}});const Z1=dt({enhance:({app:e})=>{ct("FontIcon")||e.component("FontIcon",K1),ct("BiliBili")||e.component("BiliBili",Y1)},setup:()=>{wa("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/js/brands.min.js",()=>{},{attrs:{"data-auto-replace-svg":"nest"}}),wa("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/js/solid.min.js",()=>{},{attrs:{"data-auto-replace-svg":"nest"}}),wa("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/js/fontawesome.min.js",()=>{},{attrs:{"data-auto-replace-svg":"nest"}})},rootComponents:[()=>s(X1,{})]});function ev(e,t,l){var n,a,i;t===void 0&&(t=50),l===void 0&&(l={});var r=(n=l.isImmediate)!=null&&n,o=(a=l.callback)!=null&&a,c=l.maxWait,u=Date.now(),d=[];function p(){if(c!==void 0){var v=Date.now()-u;if(v+t>=c)return c-v}return t}var h=function(){var v=[].slice.call(arguments),b=this;return new Promise(function(w,x){var g=r&&i===void 0;if(i!==void 0&&clearTimeout(i),i=setTimeout(function(){if(i=void 0,u=Date.now(),!r){var P=e.apply(b,v);o&&o(P),d.forEach(function(C){return(0,C.resolve)(P)}),d=[]}},p()),g){var _=e.apply(b,v);return o&&o(_),w(_)}d.push({resolve:w,reject:x})})};return h.cancel=function(v){i!==void 0&&clearTimeout(i),d.forEach(function(b){return(0,b.reject)(v)}),d=[]},h}const tv=({headerLinkSelector:e,headerAnchorSelector:t,delay:l,offset:n=5})=>{const a=Ve(),r=ev(()=>{var w,x;const o=Math.max(window.scrollY,document.documentElement.scrollTop,document.body.scrollTop);if(Math.abs(o-0)h.some(_=>_.hash===g.hash));for(let g=0;g=(((w=_.parentElement)==null?void 0:w.offsetTop)??0)-n,B=!P||o<(((x=P.parentElement)==null?void 0:x.offsetTop)??0)-n;if(!(C&&B))continue;const V=decodeURIComponent(a.currentRoute.value.hash),T=decodeURIComponent(_.hash);if(V===T)return;if(p){for(let q=g+1;q{window.addEventListener("scroll",r)}),Yn(()=>{window.removeEventListener("scroll",r)})},hs=async(e,t)=>{const{scrollBehavior:l}=e.options;e.options.scrollBehavior=void 0,await e.replace({query:e.currentRoute.value.query,hash:t}).finally(()=>e.options.scrollBehavior=l)},lv=".vp-sidebar-link, .toc-link",nv=".header-anchor",av=200,iv=5,rv=dt({setup(){tv({headerLinkSelector:lv,headerAnchorSelector:nv,delay:av,offset:iv})}});let wc=()=>null;const Ec=Symbol(""),sv=e=>{wc=e},ov=()=>me(Ec),cv=e=>{e.provide(Ec,wc)};var uv=M({name:"AutoCatalog",props:{base:{type:String,default:""},level:{type:Number,default:3},index:Boolean},setup(e){const t=ov(),l=on({"/":{title:"目录",empty:"暂无目录"}}),n=ue(),a=Ve(),i=Vo(),r=u=>{const d=u.I;return typeof d>"u"||d},o=()=>{const u=e.base||n.value.path.replace(/\/[^/]+$/,"/"),d=a.getRoutes(),p=[];return d.filter(({meta:h,path:v})=>{if(!Ql(v,u)||v===u)return!1;if(u==="/"){const b=gt(i.value.locales).filter(w=>w!=="/");if(v==="/404.html"||b.some(w=>Ql(v,w)))return!1}return(il(v,".html")&&!il(v,"/index.html")||il(v,"/"))&&r(h)}).map(({path:h,meta:v})=>{const b=h.substring(u.length).split("/").length;return{title:v.t||"",icon:v.i,base:h.replace(/\/[^/]+\/?$/,"/"),order:v.O||null,level:il(h,"/")?b-1:b,path:h}}).filter(({title:h,level:v})=>h&&v<=e.level).sort(({title:h,level:v,path:b,order:w},{title:x,level:g,path:_,order:P})=>v-g||(il(b,"/index.html")?-1:il(_,"/index.html")?1:w===null?P===null?h.localeCompare(x):P:P===null?w:w>0?P>0?w-P:-1:P<0?w-P:1)).forEach(h=>{var v;const{base:b,level:w}=h;switch(w){case 1:p.push(h);break;case 2:{const x=p.find(g=>g.path===b);x&&(x.children??(x.children=[])).push(h);break}default:{const x=p.find(g=>g.path===b.replace(/\/[^/]+\/$/,"/"));if(x){const g=(v=x.children)==null?void 0:v.find(_=>_.path===b);g&&(g.children??(g.children=[])).push(h)}}}}),p},c=E(()=>o());return()=>s("div",{class:"vp-catalog"},[s("h2",{class:"vp-catalog-main-title"},l.value.title),c.value.length?c.value.map(({children:u=[],icon:d,path:p,title:h},v)=>[s("h3",{id:h,class:["vp-catalog-child-title",{"has-children":u.length}]},[s("a",{href:`#${h}`,class:"header-anchor","aria-hidden":!0},"#"),s(Re,{class:"vp-catalog-title",to:p},()=>[e.index?`${v+1}.`:null,d&&t?s(t,{icon:d}):null,h||p])]),u.length?s("ul",{class:"vp-catalog-child-catalogs"},u.map(({children:b=[],icon:w,path:x,title:g},_)=>s("li",{class:"vp-child-catalog"},[s("div",{class:["vp-catalog-sub-title",{"has-children":b.length}]},[s("a",{href:`#${g}`,class:"header-anchor"},"#"),s(Re,{class:"vp-catalog-title",to:x},()=>[e.index?`${v+1}.${_+1}`:null,w&&t?s(t,{icon:w}):null,g||x])]),b.length?s("div",{class:"v-sub-catalogs"},b.map(({icon:P,path:C,title:B},R)=>s(Re,{class:"vp-sub-catalog",to:C},()=>[e.index?`${v+1}.${_+1}.${R+1}`:null,P&&t?s(t,{icon:P}):null,B||C]))):null]))):null]):s("p",{class:"vp-empty-catalog"},l.value.empty)])}}),dv=dt({enhance:({app:e})=>{cv(e),ct("AutoCatalog",e)||e.component("AutoCatalog",uv)}});const pv=s("svg",{class:"external-link-icon",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",x:"0px",y:"0px",viewBox:"0 0 100 100",width:"15",height:"15"},[s("path",{fill:"currentColor",d:"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"}),s("polygon",{fill:"currentColor",points:"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"})]),xc=M({name:"ExternalLinkIcon",props:{locales:{type:Object,required:!1,default:()=>({})}},setup(e){const t=mt(),l=E(()=>e.locales[t.value]??{openInNewWindow:"open in new window"});return()=>s("span",[pv,s("span",{class:"external-link-icon-sr-only"},l.value.openInNewWindow)])}}),hv={},vv=dt({enhance({app:e}){e.component("ExternalLinkIcon",s(xc,{locales:hv}))}});/** + * NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress + * @license MIT + */const se={settings:{minimum:.08,easing:"ease",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,barSelector:'[role="bar"]',parent:"body",template:'
'},status:null,set:e=>{const t=se.isStarted();e=Ea(e,se.settings.minimum,1),se.status=e===1?null:e;const l=se.render(!t),n=l.querySelector(se.settings.barSelector),a=se.settings.speed,i=se.settings.easing;return l.offsetWidth,fv(r=>{In(n,{transform:"translate3d("+vs(e)+"%,0,0)",transition:"all "+a+"ms "+i}),e===1?(In(l,{transition:"none",opacity:"1"}),l.offsetWidth,setTimeout(function(){In(l,{transition:"all "+a+"ms linear",opacity:"0"}),setTimeout(function(){se.remove(),r()},a)},a)):setTimeout(()=>r(),a)}),se},isStarted:()=>typeof se.status=="number",start:()=>{se.status||se.set(0);const e=()=>{setTimeout(()=>{se.status&&(se.trickle(),e())},se.settings.trickleSpeed)};return se.settings.trickle&&e(),se},done:e=>!e&&!se.status?se:se.inc(.3+.5*Math.random()).set(1),inc:e=>{let t=se.status;return t?(typeof e!="number"&&(e=(1-t)*Ea(Math.random()*t,.1,.95)),t=Ea(t+e,0,.994),se.set(t)):se.start()},trickle:()=>se.inc(Math.random()*se.settings.trickleRate),render:e=>{if(se.isRendered())return document.getElementById("nprogress");fs(document.documentElement,"nprogress-busy");const t=document.createElement("div");t.id="nprogress",t.innerHTML=se.settings.template;const l=t.querySelector(se.settings.barSelector),n=e?"-100":vs(se.status||0),a=document.querySelector(se.settings.parent);return In(l,{transition:"all 0 linear",transform:"translate3d("+n+"%,0,0)"}),a!==document.body&&fs(a,"nprogress-custom-parent"),a==null||a.appendChild(t),t},remove:()=>{gs(document.documentElement,"nprogress-busy"),gs(document.querySelector(se.settings.parent),"nprogress-custom-parent");const e=document.getElementById("nprogress");e&&gv(e)},isRendered:()=>!!document.getElementById("nprogress")},Ea=(e,t,l)=>el?l:e,vs=e=>(-1+e)*100,fv=function(){const e=[];function t(){const l=e.shift();l&&l(t)}return function(l){e.push(l),e.length===1&&t()}}(),In=function(){const e=["Webkit","O","Moz","ms"],t={};function l(r){return r.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(o,c){return c.toUpperCase()})}function n(r){const o=document.body.style;if(r in o)return r;let c=e.length;const u=r.charAt(0).toUpperCase()+r.slice(1);let d;for(;c--;)if(d=e[c]+u,d in o)return d;return r}function a(r){return r=l(r),t[r]??(t[r]=n(r))}function i(r,o,c){o=a(o),r.style[o]=c}return function(r,o){for(const c in o){const u=o[c];u!==void 0&&Object.prototype.hasOwnProperty.call(o,c)&&i(r,c,u)}}}(),Lc=(e,t)=>(typeof e=="string"?e:Di(e)).indexOf(" "+t+" ")>=0,fs=(e,t)=>{const l=Di(e),n=l+t;Lc(l,t)||(e.className=n.substring(1))},gs=(e,t)=>{const l=Di(e);if(!Lc(e,t))return;const n=l.replace(" "+t+" "," ");e.className=n.substring(1,n.length-1)},Di=e=>(" "+(e.className||"")+" ").replace(/\s+/gi," "),gv=e=>{e&&e.parentNode&&e.parentNode.removeChild(e)};const mv=()=>{ke(()=>{const e=Ve(),t=new Set;t.add(e.currentRoute.value.path),e.beforeEach(l=>{t.has(l.path)||se.start()}),e.afterEach(l=>{t.add(l.path),se.done()})})},yv=dt({setup(){mv()}}),bv=JSON.parse('{"encrypt":{},"author":{"name":"levy"},"pageInfo":["Date","Tag"],"logo":"/logo.png","repo":"levy9527/blog","docsDir":"src","displayFooter":true,"editLink":false,"darkmode":"disable","blog":{"description":"Javascript/Java/Python\\n都能撸的“全干工程师”","avatar":"https://avatars.githubusercontent.com/u/9384365?v=4","roundAvatar":true,"intro":"/about.html","medias":{"GitHub":"https://github.com/levy9527","BiliBili":"https://search.bilibili.com/video?keyword=levydotvip&from_source=webtop_search","Email":"mailto:info@897895407@qq.com","Rss":"https://levy.vip/rss.xml"}},"locales":{"/":{"lang":"zh-CN","navbarLocales":{"langName":"简体中文","selectLangAriaLabel":"选择语言"},"metaLocales":{"author":"作者","date":"写作日期","origin":"原创","views":"访问量","category":"分类","tag":"标签","readingTime":"阅读时间","words":"字数","toc":"此页内容","prev":"上一页","next":"下一页","lastUpdated":"上次编辑于","contributors":"贡献者","editLink":"编辑此页","print":"打印"},"blogLocales":{"article":"文章","articleList":"文章列表","category":"分类","tag":"标签","timeline":"时间轴","timelineTitle":"昨日不在","all":"全部","intro":"个人介绍","star":"收藏"},"paginationLocales":{"prev":"上一页","next":"下一页","navigate":"跳转到","action":"前往","errorText":"请输入 1 到 $page 之前的页码!"},"outlookLocales":{"themeColor":"主题色","darkmode":"外观","fullscreen":"全屏"},"routeLocales":{"skipToContent":"跳至主要內容","notFoundTitle":"页面不存在","notFoundMsg":["这里什么也没有","我们是怎么来到这儿的?","这 是 四 零 四 !","看起来你访问了一个失效的链接"],"back":"返回上一页","home":"带我回家","openInNewWindow":"Open in new window"},"navbar":[{"text":"首页","icon":"house","link":"/"},{"text":"分类","icon":"list","children":[{"text":"软件研发","icon":"code","link":"/tools/how-to-connect-to-internet"},{"text":"工作日常","icon":"briefcase","link":"/daily"},{"text":"英语学习","icon":"globe","link":"/english"}]},"/about"],"sidebar":{"/daily/":"structure","/english":["let-chatgpt-be-your-foreign-language-teacher","how-to-self-evaluate-english-level","learning-7000-words-task-completed","everyone-can-learn-english-1-overview","everyone-can-learn-english-2-pronunciation","everyone-can-learn-english-3-words","everyone-can-learn-english-4-listening-and-speaking","everyone-can-learn-english-5-reading-and-writing","contemporary-college-english-1","contemporary-college-english-2","contemporary-college-english-3","contemporary-college-english-4","contemporary-college-english-5","contemporary-college-english-6"],"/":[{"text":"实用工具","prefix":"/tools/","children":["how-to-connect-to-internet"]},{"text":"Git相关","prefix":"/git/","children":["git-useful-commands","git-definitive-guide-to-merge-code","git-history-two-tricks-in-idea","gitlab-ci","rethinking-git-flow","git-best-pratices","use-command-line-tool-to-manage-gitlab-merge-request"]},{"text":"软件测试","prefix":"/software-testing/","children":["unit-testing-overview","use-postman-for-api-testing","use-RestAssured-for-api-testing","use-jest-for-test-driven-development","use-playwright-for-ui-testing","use-cypress-for-e2e-testing"]},{"text":"MySQL","prefix":"/mysql","children":["mysql-backup-case-study-mysqldump-in-action","mysql-data-migration-case-study-add-auto-increment","mysql-details-you-should-know-when-execute-sql-in-command-line"]},{"text":"Java","prefix":"/java","children":["Resolving-Common-Problems-in-IntelliJ-IDEA","Resolving-Common-Problems-in-Maven.md","using-enum-in-java","which-one-is-better-Boolean-or-boolean","which-one-is-better-forEach-or-map","recommend-practices-for-collections-naming-convention","recommend-practices-for-writing-good-functions","avoid-sending-password-in-plaintext","recommend-practices-for-query-by-date-range","common-practices-for-handling-excel","check-if-name-exists","how-to-convert-snapshot-into-release-jar-without-source-code","why-i-prefer-fastjson-instead-of-jackson","why-is-it-so-hard-to-upgrade-dependencies"]},{"text":"Python","prefix":"/python","children":["export-mysql-table-into-excel"]},{"text":"前端技术","prefix":"/frontend","children":["old-articles","performance-optimization-in-action"]}]}}}}'),_v=W(bv),Tc=()=>_v,Ac=Symbol(""),kv=()=>{const e=me(Ac);if(!e)throw new Error("useThemeLocaleData() is called without provider.");return e},wv=(e,t)=>{const{locales:l,...n}=e;return{...n,...l==null?void 0:l[t]}},Ev=dt({enhance({app:e}){const t=Tc(),l=e._context.provides[Ti],n=E(()=>wv(t.value,l.value));e.provide(Ac,n),Object.defineProperties(e.config.globalProperties,{$theme:{get(){return t.value}},$themeLocale:{get(){return n.value}}})}});const xv=800,Lv=2e3,Tv={"/":{copy:"复制代码",copied:"已复制",hint:"复制成功"}},Av=!1,Iv=['.theme-hope-content div[class*="language-"] pre'],ms=!1,xa=new Map,Pv=()=>{const{copy:e}=O1({legacy:!0}),t=on(Tv),l=ue(),n=l1(),a=o=>{if(!o.hasAttribute("copy-code-registered")){const c=document.createElement("button");c.type="button",c.classList.add("copy-code-button"),c.innerHTML='
',c.setAttribute("aria-label",t.value.copy),c.setAttribute("data-copied",t.value.copied),o.parentElement&&o.parentElement.insertBefore(c,o),o.setAttribute("copy-code-registered","")}},i=()=>Tl().then(()=>new Promise(o=>{setTimeout(()=>{Iv.forEach(c=>{document.querySelectorAll(c).forEach(a)}),o()},xv)})),r=(o,c,u)=>{let{innerText:d=""}=c;/language-(shellscript|shell|bash|sh|zsh)/.test(o.classList.toString())&&(d=d.replace(/^ *(\$|>) /gm,"")),e(d).then(()=>{u.classList.add("copied"),clearTimeout(xa.get(u));const p=setTimeout(()=>{u.classList.remove("copied"),u.blur(),xa.delete(u)},Lv);xa.set(u,p)})};ke(()=>{(!n.value||ms)&&i(),Me("click",o=>{const c=o.target;if(c.matches('div[class*="language-"] > button.copy')){const u=c.parentElement,d=c.nextElementSibling;d&&r(u,d,c)}else if(c.matches('div[class*="language-"] div.copy-icon')){const u=c.parentElement,d=u.parentElement,p=u.nextElementSibling;p&&r(d,p,u)}}),ce(()=>l.value.path,()=>{(!n.value||ms)&&i()})})};var Ov=dt({setup:()=>{Pv()}});const Pn=Ri("VUEPRESS_CODE_TAB_STORE",{});var Cv=M({name:"CodeTabs",props:{active:{type:Number,default:0},data:{type:Array,required:!0},id:{type:String,required:!0},tabId:{type:String,default:""}},slots:Object,setup(e,{slots:t}){const l=W(e.active),n=Ue([]),a=()=>{e.tabId&&(Pn.value[e.tabId]=e.data[l.value].id)},i=(u=l.value)=>{l.value=u{l.value=u>0?u-1:n.value.length-1,n.value[l.value].focus()},o=(u,d)=>{u.key===" "||u.key==="Enter"?(u.preventDefault(),l.value=d):u.key==="ArrowRight"?(u.preventDefault(),i()):u.key==="ArrowLeft"&&(u.preventDefault(),r()),e.tabId&&(Pn.value[e.tabId]=e.data[l.value].id)},c=()=>{if(e.tabId){const u=e.data.findIndex(({id:d})=>Pn.value[e.tabId]===d);if(u!==-1)return u}return e.active};return ke(()=>{l.value=c(),ce(()=>Pn.value[e.tabId],(u,d)=>{if(e.tabId&&u!==d){const p=e.data.findIndex(({id:h})=>h===u);p!==-1&&(l.value=p)}})}),()=>e.data.length?s("div",{class:"vp-code-tabs"},[s("div",{class:"vp-code-tabs-nav",role:"tablist"},e.data.map(({id:u},d)=>{const p=d===l.value;return s("button",{type:"button",ref:h=>{h&&(n.value[d]=h)},class:["vp-code-tab-nav",{active:p}],role:"tab","aria-controls":`codetab-${e.id}-${d}`,"aria-selected":p,onClick:()=>{l.value=d,a()},onKeydown:h=>o(h,d)},t[`title${d}`]({value:u,isActive:p}))})),e.data.map(({id:u},d)=>{const p=d===l.value;return s("div",{class:["vp-code-tab",{active:p}],id:`codetab-${e.id}-${d}`,role:"tabpanel","aria-expanded":p},t[`tab${d}`]({value:u,isActive:p}))})]):null}});const Ic=({active:e=!1},{slots:t})=>{var l;return s("div",{class:["code-group-item",{active:e}],"aria-selected":e},(l=t.default)==null?void 0:l.call(t))};Ic.displayName="CodeGroupItem";const Rv=M({name:"CodeGroup",slots:Object,setup(e,{slots:t}){const l=W(-1),n=Ue([]),a=(o=l.value)=>{l.value=o{l.value=o>0?o-1:n.value.length-1,n.value[l.value].focus()},r=(o,c)=>{o.key===" "||o.key==="Enter"?(o.preventDefault(),l.value=c):o.key==="ArrowRight"?(o.preventDefault(),a(c)):o.key==="ArrowLeft"&&(o.preventDefault(),i(c))};return()=>{var o;const c=(((o=t.default)==null?void 0:o.call(t))||[]).filter(u=>u.type.name==="CodeGroupItem").map(u=>(u.props===null&&(u.props={}),u));return c.length===0?null:(l.value<0||l.value>c.length-1?(l.value=c.findIndex(u=>"active"in u.props),l.value===-1&&(l.value=0)):c.forEach((u,d)=>{u.props.active=d===l.value}),s("div",{class:"code-group"},[s("div",{class:"code-group-nav"},c.map((u,d)=>{const p=d===l.value;return s("button",{type:"button",ref:h=>{h&&(n.value[d]=h)},class:["code-group-nav-tab",{active:p}],"aria-pressed":p,"aria-expanded":p,onClick:()=>{l.value=d},onKeydown:h=>r(h,d)},u.props.title)})),c]))}}});const La=Ri("VUEPRESS_TAB_STORE",{});var Sv=M({name:"Tabs",props:{active:{type:Number,default:0},data:{type:Array,required:!0},id:{type:String,required:!0},tabId:{type:String,default:""}},slots:Object,setup(e,{slots:t}){const l=W(e.active),n=Ue([]),a=()=>{e.tabId&&(La.value[e.tabId]=e.data[l.value].id)},i=(u=l.value)=>{l.value=u{l.value=u>0?u-1:n.value.length-1,n.value[l.value].focus()},o=(u,d)=>{u.key===" "||u.key==="Enter"?(u.preventDefault(),l.value=d):u.key==="ArrowRight"?(u.preventDefault(),i()):u.key==="ArrowLeft"&&(u.preventDefault(),r()),a()},c=()=>{if(e.tabId){const u=e.data.findIndex(({id:d})=>La.value[e.tabId]===d);if(u!==-1)return u}return e.active};return ke(()=>{l.value=c(),ce(()=>La.value[e.tabId],(u,d)=>{if(e.tabId&&u!==d){const p=e.data.findIndex(({id:h})=>h===u);p!==-1&&(l.value=p)}})}),()=>e.data.length?s("div",{class:"vp-tabs"},[s("div",{class:"vp-tabs-nav",role:"tablist"},e.data.map(({id:u},d)=>{const p=d===l.value;return s("button",{type:"button",ref:h=>{h&&(n.value[d]=h)},class:["vp-tab-nav",{active:p}],role:"tab","aria-controls":`tab-${e.id}-${d}`,"aria-selected":p,onClick:()=>{l.value=d,a()},onKeydown:h=>o(h,d)},t[`title${d}`]({value:u,isActive:p}))})),e.data.map(({id:u},d)=>{const p=d===l.value;return s("div",{class:["vp-tab",{active:p}],id:`tab-${e.id}-${d}`,role:"tabpanel","aria-expanded":p},t[`tab${d}`]({value:u,isActive:p}))})]):null}});const Dv=dt({enhance:({app:e})=>{e.component("CodeTabs",Cv),ct("CodeGroup",e)||e.component("CodeGroup",Rv),ct("CodeGroupItem",e)||e.component("CodeGroupItem",Ic),e.component("Tabs",Sv)},setup:()=>{}});let $v={};const Pc=Symbol(""),Mv=()=>me(Pc),Vv=e=>{e.provide(Pc,$v)};const Fv=".theme-hope-content :not(a) > img:not([no-view])",Nv={"/":{closeTitle:"关闭",downloadTitle:"下载图片",fullscreenTitle:"切换全屏",zoomTitle:"缩放",arrowPrevTitle:"上一个 (左箭头)",arrowNextTitle:"下一个 (右箭头)"}},jv=800,Bv='
',zv=e=>ae(e)?Array.from(document.querySelectorAll(e)):e.map(t=>Array.from(document.querySelectorAll(t))).flat(),Oc=e=>new Promise((t,l)=>{e.complete?t({type:"image",element:e,src:e.src,width:e.naturalWidth,height:e.naturalHeight,alt:e.alt,msrc:e.src}):(e.onload=()=>t(Oc(e)),e.onerror=n=>l(n))}),Hv=()=>{const{isSupported:e,toggle:t}=Si(),l=Mv(),n=on(Nv),a=ue();let i;const r=c=>{c.on("uiRegister",()=>{e&&c.ui.registerElement({name:"fullscreen",order:7,isButton:!0,html:'',onClick:()=>{t()}}),c.ui.registerElement({name:"download",order:8,isButton:!0,tagName:"a",html:{isCustomSVG:!0,inner:'',outlineID:"pswp__icn-download"},onInit:(u,d)=>{u.setAttribute("download",""),u.setAttribute("target","_blank"),u.setAttribute("rel","noopener"),d.on("change",()=>{u.setAttribute("href",d.currSlide.data.src)})}}),c.ui.registerElement({name:"bulletsIndicator",className:"photo-swipe-bullets-indicator",appendTo:"wrapper",onInit:(u,d)=>{const p=[];let h=-1;for(let v=0;v{d.goTo(p.indexOf(w.target))},p.push(b),u.appendChild(b)}d.on("change",()=>{h>=0&&p[h].classList.remove("active"),p[d.currIndex].classList.add("active"),h=d.currIndex})}})})},o=()=>Promise.all([y(()=>import("./photoswipe.esm-5794cde2.js"),[]),Tl().then(()=>new Promise(c=>setTimeout(c,jv)).then(()=>zv(Fv)))]).then(([{default:c},u])=>{const d=u.map(p=>({html:Bv,element:p,msrc:p.src}));u.forEach((p,h)=>{const v=()=>{i=new c({preloaderDelay:0,showHideAnimationType:"zoom",...n.value,...l,dataSource:d,index:h,closeOnVerticalDrag:!0,wheelToZoom:!1}),r(i),i.addFilter("thumbEl",()=>p),i.addFilter("placeholderSrc",()=>p.src),i.init()};p.style.cursor="zoom-in",p.addEventListener("click",()=>{v()}),p.addEventListener("keypress",({key:b})=>{b==="Enter"&&v()})}),u.forEach((p,h)=>{Oc(p).then(v=>{d.splice(h,1,v),i==null||i.refreshSlideContent(h)})})});ke(()=>{Me("wheel",()=>{i==null||i.close()}),o(),ce(()=>a.value.path,()=>o())})};var qv=dt({enhance:({app:e})=>{Vv(e)},setup:()=>{Hv()}});const Cc=()=>{const e=ue();return E(()=>e.value.readingTime??null)},Ua=typeof{"/":{word:"约 $word 字",less1Minute:"小于 1 分钟",time:"大约 $time 分钟"}}>"u"?null:{"/":{word:"约 $word 字",less1Minute:"小于 1 分钟",time:"大约 $time 分钟"}},Rc=(e,t)=>{const{minutes:l,words:n}=e,{less1Minute:a,word:i,time:r}=t;return{time:l<1?a:r.replace("$time",Math.round(l).toString()),words:i.replace("$word",n.toString())}},ys={words:"",time:""},Sc=()=>Ua?on(Ua):E(()=>null),Uv=()=>{if(typeof Ua>"u")return E(()=>ys);const e=Cc(),t=Sc();return E(()=>e.value&&t.value?Rc(e.value,t.value):ys)},el=()=>Tc(),ie=()=>kv(),Il=()=>E(()=>!!el().value.pure);var Ta=M({name:"EmptyComponent",setup:()=>()=>null});const Gv="719px",Wv="1440px",Kv="false",$i={mobileBreakPoint:Gv,pcBreakPoint:Wv,enableThemeColor:Kv},Mi={"/daily/":["testing-environments-should-be-consistent-with-production-environments","copy-code-may-not-be-guilty","you-dont-need-to-add-tenant_id-to-every-table","reflections-on-a-speech-by-cto-of-microsoft-china","iteration-retrospective-of-sanyuan","leverage-ai-to-boost-coding-productivity","claude-ai-in-action-extract-info-from-html","beyond-utf8-do-you-know-utf8mb4-and-collation","use-claude2-instead-of-chatgpt","vim-creator-pass-away","dont-try-to-argue-with-a-sb","what-is-the-difference-between-sh-and-bash","about-arm-things-you-need-to-know","a-vuepress2-entertaining-video","a-warning-from-navicat","things-I-have-to-vent-about-vue"]},Dc=e=>{const{icon:t="",color:l,size:n}=e,a={};return l&&(a.color=l),n&&(a.height=Number.isNaN(Number(n))?n:`${n}px`),Zt(t)?s("img",{class:"icon",src:t,"no-view":"",style:a}):na(t)?s("img",{class:"icon",src:Le(t),"no-view":"",style:a}):s(Je("FontIcon"),e)};Dc.displayName="HopeIcon";var Ne=Dc,be=(e=>(e.type="y",e.title="t",e.shortTitle="s",e.icon="i",e.author="a",e.date="d",e.localizedDate="l",e.category="c",e.tag="g",e.isEncrypted="n",e.isOriginal="o",e.readingTime="r",e.excerpt="e",e.sticky="u",e.cover="v",e.index="I",e.order="O",e))(be||{}),$c=(e=>(e.article="a",e.home="h",e.slide="s",e.page="p",e))($c||{});const pl=(e,t,l=!1)=>{const n=encodeURI(t);let a=_l(e,tc(n));a.name==="404"&&(a=_l(e,n));const{fullPath:i,meta:r,name:o}=a;return{text:!l&&r[be.shortTitle]?r[be.shortTitle]:r[be.title]||t,link:o==="404"?t:i,...r[be.icon]?{icon:r[be.icon]}:{}}},un=()=>{const e=Ve(),t=yt();return l=>{if(l)if(na(l))t.path!==l&&e.push(l);else if(Zt(l)||Io(l))window&&window.open(l);else{const n=t.path.slice(0,t.path.lastIndexOf("/"));e.push(`${n}/${encodeURI(l)}`)}}},Mc=()=>{const e=ie(),t=Ee();return E(()=>{const{author:l}=t.value;return l?Yl(l):l===!1?[]:Yl(e.value.author,!1)})},Jv=()=>{const e=Ee();return E(()=>nc(e.value.category).map(t=>{var l,n;return{name:t,path:((n=(l=me(Symbol.for("categoryMap")))==null?void 0:l.value.map[t])==null?void 0:n.path)||""}}))},Qv=()=>{const e=Ee();return E(()=>ac(e.value.tag).map(t=>{var l,n;return{name:t,path:((n=(l=me(Symbol.for("tagMap")))==null?void 0:l.value.map[t])==null?void 0:n.path)||""}}))},Yv=()=>{const e=Ee(),t=ue();return E(()=>{const l=Pi(e.value.date);if(l)return l;const{createdTime:n}=t.value.git||{};return n?new Date(n):null})},Xv=()=>{const e=ie(),t=ue(),l=Ee(),n=Mc(),a=Jv(),i=Qv(),r=Yv(),o=Cc(),c=Uv(),u=E(()=>({author:n.value,category:a.value,date:r.value,localizedDate:t.value.localizedDate,tag:i.value,isOriginal:l.value.isOriginal||!1,readingTime:o.value,readingTimeLocale:c.value,pageview:"pageview"in l.value?l.value.pageview:!0})),d=E(()=>"pageInfo"in l.value?l.value.pageInfo:"pageInfo"in e.value?e.value.pageInfo:null);return{info:u,items:d}},{mobileBreakPoint:Zv,pcBreakPoint:ef}=$i,bs=e=>e.endsWith("px")?Number(e.slice(0,-2)):null,dn=()=>{const e=W(!1),t=W(!1),l=()=>{e.value=window.innerWidth<=(bs(Zv)??719),t.value=window.innerWidth>=(bs(ef)??1440)};return ke(()=>{l(),Me("resize",l,!1),Me("orientationchange",l,!1)}),{isMobile:e,isPC:t}},Vc=Symbol(""),pn=()=>{const e=me(Vc);if(!e)throw new Error("useDarkmode() is called without provider.");return e},tf=e=>{const t=el(),l=N1(),n=Ri("vuepress-theme-hope-scheme","auto"),a=E(()=>t.value.darkmode||"switch"),i=E(()=>{const o=a.value;return o==="disable"?!1:o==="enable"?!0:o==="auto"?l.value:o==="toggle"?n.value==="dark":n.value==="dark"||n.value==="auto"&&l.value}),r=E(()=>{const o=a.value;return o==="switch"||o==="toggle"});e.provide(Vc,{canToggle:r,config:a,isDarkmode:i,status:n}),Object.defineProperties(e.config.globalProperties,{$isDarkmode:{get:()=>i.value}})},lf=()=>{const{isDarkmode:e}=pn(),t=(l=e.value)=>document.documentElement.setAttribute("data-theme",l?"dark":"light");ke(()=>{ce(e,t,{immediate:!0})})};var He=M({name:"AutoLink",inheritAttrs:!1,props:{config:{type:Object,required:!0},exact:Boolean,noExternalLinkIcon:Boolean},emits:["focusout"],slots:Object,setup(e,{attrs:t,emit:l,slots:n}){const a=yt(),i=Vo(),r=Ll(e,"config"),o=E(()=>Zt(r.value.link)),c=E(()=>Io(r.value.link)||P0(r.value.link)),u=E(()=>c.value?void 0:r.value.target||(o.value?"_blank":void 0)),d=E(()=>u.value==="_blank"),p=E(()=>!o.value&&!c.value&&!d.value),h=E(()=>c.value?void 0:r.value.rel||(d.value?"noopener noreferrer":void 0)),v=E(()=>r.value.ariaLabel||r.value.text),b=E(()=>{if(e.exact)return!1;const x=gt(i.value.locales);return x.length?x.every(g=>g!==r.value.link):r.value.link!=="/"}),w=E(()=>p.value?r.value.activeMatch?new RegExp(r.value.activeMatch).test(a.path):b.value?Ql(a.path,r.value.link):a.path===r.value.link:!1);return()=>{const{before:x,after:g,default:_}=n,{text:P,icon:C,link:B}=r.value;return p.value?s(Re,{to:B,"aria-label":v.value,...t,class:["nav-link",{active:w.value},t.class],onFocusout:()=>l("focusout")},()=>_?_():[x?x():s(Ne,{icon:C}),P,g==null?void 0:g()]):s("a",{href:B,rel:h.value,target:u.value,"aria-label":v.value,...t,class:["nav-link",t.class],onFocusout:()=>l("focusout")},_?_():[x?x():s(Ne,{icon:C}),P,e.noExternalLinkIcon?null:s(xc),g==null?void 0:g()])}}});const kl=(e,t,l=!1)=>"activeMatch"in t?new RegExp(t.activeMatch).test(e.path):Ci(e,t.link)?!0:t.children&&!l?t.children.some(n=>kl(e,n)):!1,Fc=(e,t)=>t.type==="group"?t.children.some(l=>l.type==="group"?Fc(e,l):l.type==="page"&&kl(e,l,!0))||"prefix"in t&&Ci(e,t.prefix):!1,Nc=(e,t)=>ae(e.link)?s(He,{...t,config:e}):s("p",t,[s(Ne,{icon:e.icon}),e.text]),jc=e=>{const t=yt();return e?s("ul",{class:"vp-sidebar-sub-headers"},e.map(l=>{const n=kl(t,l,!0);return s("li",{class:"vp-sidebar-sub-header"},[Nc(l,{class:["vp-sidebar-link","vp-heading",{active:n}]}),jc(l.children)])})):null},Aa=(e="",t="")=>na(t)?t:`${T0(e)}${t}`,nf=(e,t)=>{const l=ue();return{type:"heading",text:e.title,link:`${l.value.path}#${e.slug}`,children:Vi(e.children,t)}},Vi=(e,t)=>t>0?e.map(l=>nf(l,t-1)):[],Bc=e=>{const t=ue();return Vi(t.value.headers,e)},Ga=(e,t,l="")=>{const n=Ve(),a=ue(),i=(r,o=l)=>{var c;const u=ae(r)?pl(n,Aa(o,r)):r.link?{...r,...Bn(r.link)?{}:{link:pl(n,Aa(o,r.link)).link}}:r;if("children"in u){const d=Aa(o,u.prefix),p=u.children==="structure"?Mi[d]:u.children;return{type:"group",...u,prefix:d,children:p.map(h=>i(h,d))}}return{type:"page",...u,children:u.link===a.value.path?Vi(((c=a.value.headers[0])==null?void 0:c.level)===1?a.value.headers[0].children:a.value.headers,t):[]}};return e.map(r=>i(r))},af=(e,t)=>{const l=ue(),n=gt(e).sort((a,i)=>i.length-a.length);for(const a of n)if(Ql(decodeURI(l.value.path),a)){const i=e[a];return i?Ga(i==="structure"?Mi[a]:i==="heading"?Bc(t):i,t,a):[]}return console.warn(`${l.value.path} is missing sidebar config.`),[]},rf=(e,t)=>{const l=mt();return e===!1?[]:e==="heading"?Bc(t):e==="structure"?Ga(Mi[l.value],t,l.value):X(e)?Ga(e,t):xi(e)?af(e,t):[]},zc=Symbol(""),sf=()=>{const e=Ee(),t=ie(),l=E(()=>e.value.home?!1:e.value.sidebar??t.value.sidebar??"structure"),n=E(()=>e.value.headerDepth??t.value.headerDepth??2),a=E(()=>rf(l.value,n.value));ot(zc,a)},Fi=()=>{const e=me(zc);if(!e)throw new Error("useSidebarItems() is called without provider.");return e};var of=M({name:"PageFooter",setup(){const e=Ee(),t=ie(),l=Mc(),n=E(()=>{const{copyright:r,footer:o}=e.value;return o!==!1&&!!(r||o||t.value.displayFooter)}),a=E(()=>{const{footer:r}=e.value;return r===!1?!1:ae(r)?r:t.value.footer||""}),i=E(()=>"copyright"in e.value?e.value.copyright:"copyright"in t.value?t.value.copyright:l.value.length?`Copyright © ${new Date().getFullYear()} ${l.value[0].name}`:!1);return()=>n.value?s("footer",{class:"vp-footer-wrapper"},[a.value?s("div",{class:"vp-footer",innerHTML:a.value}):null,i.value?s("div",{class:"vp-copyright",innerHTML:i.value}):null]):null}}),cf=M({name:"NavbarDropdownLink",props:{config:{type:Object,required:!0}},slots:Object,setup(e,{slots:t}){const l=ue(),n=Ll(e,"config"),a=E(()=>n.value.ariaLabel||n.value.text),i=W(!1);ce(()=>l.value.path,()=>{i.value=!1});const r=o=>{o.detail===0&&(i.value=!i.value)};return()=>{var o;return s("div",{class:["dropdown-wrapper",{open:i.value}]},[s("button",{type:"button",class:"dropdown-title","aria-label":a.value,onClick:r},[((o=t.title)==null?void 0:o.call(t))||s("span",{class:"title"},[s(Ne,{icon:n.value.icon}),e.config.text]),s("span",{class:"arrow"}),s("ul",{class:"nav-dropdown"},n.value.children.map((c,u)=>{const d=u===n.value.children.length-1;return s("li",{class:"dropdown-item"},"children"in c?[s("h4",{class:"dropdown-subtitle"},c.link?s(He,{config:c,onFocusout:()=>{c.children.length===0&&d&&(i.value=!1)}}):s("span",c.text)),s("ul",{class:"dropdown-subitem-wrapper"},c.children.map((p,h)=>s("li",{class:"dropdown-subitem"},s(He,{config:p,onFocusout:()=>{h===c.children.length-1&&d&&(i.value=!1)}}))))]:s(He,{config:c,onFocusout:()=>{d&&(i.value=!1)}}))}))])])}}});const Hc=(e,t,l="")=>ae(t)?pl(e,`${l}${t}`):"children"in t?{...t,...t.link&&!Bn(t.link)?pl(e,`${l}${t.link}`):{},children:t.children.map(n=>Hc(e,n,`${l}${t.prefix||""}`))}:{...t,link:Bn(t.link)?t.link:pl(e,`${l}${t.link}`).link},qc=()=>{const e=ie(),t=Ve(),l=()=>(e.value.navbar||[]).map(a=>Hc(t,a)),n=W(l());return ce(e,()=>{n.value=l()}),n},uf=()=>{const e=ie(),t=E(()=>e.value.repo||null),l=E(()=>t.value?Zh(t.value):null),n=E(()=>t.value?rc(t.value):null),a=E(()=>l.value?e.value.repoLabel??(n.value===null?"Source":n.value):null);return E(()=>!l.value||!a.value||e.value.repoDisplay===!1?null:{type:n.value||"Source",label:a.value,link:l.value})};var df=M({name:"NavScreenDropdown",props:{config:{type:Object,required:!0}},setup(e){const t=ue(),l=Ll(e,"config"),n=E(()=>l.value.ariaLabel||l.value.text),a=W(!1);ce(()=>t.value.path,()=>{a.value=!1});const i=(r,o)=>o[o.length-1]===r;return()=>[s("button",{type:"button",class:["nav-screen-dropdown-title",{active:a.value}],"aria-label":n.value,onClick:()=>{a.value=!a.value}},[s("span",{class:"title"},[s(Ne,{icon:l.value.icon}),e.config.text]),s("span",{class:["arrow",a.value?"down":"end"]})]),s("ul",{class:["nav-screen-dropdown",{hide:!a.value}]},l.value.children.map(r=>s("li",{class:"dropdown-item"},"children"in r?[s("h4",{class:"dropdown-subtitle"},r.link?s(He,{config:r,onFocusout:()=>{i(r,l.value.children)&&r.children.length===0&&(a.value=!1)}}):s("span",r.text)),s("ul",{class:"dropdown-subitem-wrapper"},r.children.map(o=>s("li",{class:"dropdown-subitem"},s(He,{config:o,onFocusout:()=>{i(o,r.children)&&i(r,l.value.children)&&(a.value=!1)}}))))]:s(He,{config:r,onFocusout:()=>{i(r,l.value.children)&&(a.value=!1)}}))))]}}),pf=M({name:"NavScreenLinks",setup(){const e=qc();return()=>e.value.length?s("nav",{class:"nav-screen-links"},e.value.map(t=>s("div",{class:"navbar-links-item"},"children"in t?s(df,{config:t}):s(He,{config:t})))):null}});const Uc=()=>s(de,{name:"dark"},()=>s("path",{d:"M524.8 938.667h-4.267a439.893 439.893 0 0 1-313.173-134.4 446.293 446.293 0 0 1-11.093-597.334A432.213 432.213 0 0 1 366.933 90.027a42.667 42.667 0 0 1 45.227 9.386 42.667 42.667 0 0 1 10.24 42.667 358.4 358.4 0 0 0 82.773 375.893 361.387 361.387 0 0 0 376.747 82.774 42.667 42.667 0 0 1 54.187 55.04 433.493 433.493 0 0 1-99.84 154.88 438.613 438.613 0 0 1-311.467 128z"}));Uc.displayName="DarkIcon";const Gc=()=>s(de,{name:"light"},()=>s("path",{d:"M952 552h-80a40 40 0 0 1 0-80h80a40 40 0 0 1 0 80zM801.88 280.08a41 41 0 0 1-57.96-57.96l57.96-58a41.04 41.04 0 0 1 58 58l-58 57.96zM512 752a240 240 0 1 1 0-480 240 240 0 0 1 0 480zm0-560a40 40 0 0 1-40-40V72a40 40 0 0 1 80 0v80a40 40 0 0 1-40 40zm-289.88 88.08-58-57.96a41.04 41.04 0 0 1 58-58l57.96 58a41 41 0 0 1-57.96 57.96zM192 512a40 40 0 0 1-40 40H72a40 40 0 0 1 0-80h80a40 40 0 0 1 40 40zm30.12 231.92a41 41 0 0 1 57.96 57.96l-57.96 58a41.04 41.04 0 0 1-58-58l58-57.96zM512 832a40 40 0 0 1 40 40v80a40 40 0 0 1-80 0v-80a40 40 0 0 1 40-40zm289.88-88.08 58 57.96a41.04 41.04 0 0 1-58 58l-57.96-58a41 41 0 0 1 57.96-57.96z"}));Gc.displayName="LightIcon";const Wc=()=>s(de,{name:"auto"},()=>s("path",{d:"M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm0-840c-198.78 0-360 161.22-360 360 0 198.84 161.22 360 360 360s360-161.16 360-360c0-198.78-161.22-360-360-360zm0 660V212c165.72 0 300 134.34 300 300 0 165.72-134.28 300-300 300z"}));Wc.displayName="AutoIcon";const Kc=()=>s(de,{name:"enter-fullscreen"},()=>s("path",{d:"M762.773 90.24h-497.28c-96.106 0-174.4 78.293-174.4 174.4v497.28c0 96.107 78.294 174.4 174.4 174.4h497.28c96.107 0 175.04-78.293 174.4-174.4V264.64c0-96.213-78.186-174.4-174.4-174.4zm-387.2 761.173H215.04c-21.867 0-40.427-17.92-41.067-41.066V649.92c0-22.507 17.92-40.427 40.427-40.427 11.307 0 21.227 4.694 28.48 11.947 7.253 7.253 11.947 17.92 11.947 28.48v62.293l145.28-145.28c15.893-15.893 41.813-15.893 57.706 0 15.894 15.894 15.894 41.814 0 57.707l-145.28 145.28h62.294c22.506 0 40.426 17.92 40.426 40.427s-17.173 41.066-39.68 41.066zM650.24 165.76h160.427c21.866 0 40.426 17.92 41.066 41.067v160.426c0 22.507-17.92 40.427-40.426 40.427-11.307 0-21.227-4.693-28.48-11.947-7.254-7.253-11.947-17.92-11.947-28.48v-62.186L625.6 450.347c-15.893 15.893-41.813 15.893-57.707 0-15.893-15.894-15.893-41.814 0-57.707l145.28-145.28H650.88c-22.507 0-40.427-17.92-40.427-40.427s17.174-41.173 39.787-41.173z"}));Kc.displayName="EnterFullScreenIcon";const Jc=()=>s(de,{name:"cancel-fullscreen"},()=>s("path",{d:"M778.468 78.62H247.922c-102.514 0-186.027 83.513-186.027 186.027V795.08c0 102.514 83.513 186.027 186.027 186.027h530.432c102.514 0 186.71-83.513 186.026-186.027V264.647C964.494 162.02 880.981 78.62 778.468 78.62zM250.88 574.35h171.122c23.324 0 43.122 19.115 43.804 43.805v171.121c0 24.008-19.114 43.122-43.122 43.122-12.06 0-22.641-5.006-30.378-12.743s-12.743-19.115-12.743-30.379V722.83L224.597 877.91c-16.953 16.952-44.6 16.952-61.553 0-16.953-16.954-16.953-44.602 0-61.554L318.009 661.39h-66.446c-24.007 0-43.122-19.114-43.122-43.122 0-24.12 18.432-43.918 42.439-43.918zm521.899-98.873H601.657c-23.325 0-43.122-19.114-43.805-43.804V260.55c0-24.007 19.115-43.122 43.122-43.122 12.06 0 22.642 5.007 30.379 12.743s12.743 19.115 12.743 30.38v66.445l154.965-154.965c16.953-16.953 44.601-16.953 61.554 0 16.953 16.953 16.953 44.6 0 61.554L705.536 388.55h66.446c24.007 0 43.122 19.115 43.122 43.122.114 24.007-18.318 43.804-42.325 43.804z"}));Jc.displayName="CancelFullScreenIcon";const Qc=()=>s(de,{name:"outlook"},()=>[s("path",{d:"M224 800c0 9.6 3.2 44.8 6.4 54.4 6.4 48-48 76.8-48 76.8s80 41.6 147.2 0 134.4-134.4 38.4-195.2c-22.4-12.8-41.6-19.2-57.6-19.2C259.2 716.8 227.2 761.6 224 800zM560 675.2l-32 51.2c-51.2 51.2-83.2 32-83.2 32 25.6 67.2 0 112-12.8 128 25.6 6.4 51.2 9.6 80 9.6 54.4 0 102.4-9.6 150.4-32l0 0c3.2 0 3.2-3.2 3.2-3.2 22.4-16 12.8-35.2 6.4-44.8-9.6-12.8-12.8-25.6-12.8-41.6 0-54.4 60.8-99.2 137.6-99.2 6.4 0 12.8 0 22.4 0 12.8 0 38.4 9.6 48-25.6 0-3.2 0-3.2 3.2-6.4 0-3.2 3.2-6.4 3.2-6.4 6.4-16 6.4-16 6.4-19.2 9.6-35.2 16-73.6 16-115.2 0-105.6-41.6-198.4-108.8-268.8C704 396.8 560 675.2 560 675.2zM224 419.2c0-28.8 22.4-51.2 51.2-51.2 28.8 0 51.2 22.4 51.2 51.2 0 28.8-22.4 51.2-51.2 51.2C246.4 470.4 224 448 224 419.2zM320 284.8c0-22.4 19.2-41.6 41.6-41.6 22.4 0 41.6 19.2 41.6 41.6 0 22.4-19.2 41.6-41.6 41.6C339.2 326.4 320 307.2 320 284.8zM457.6 208c0-12.8 12.8-25.6 25.6-25.6 12.8 0 25.6 12.8 25.6 25.6 0 12.8-12.8 25.6-25.6 25.6C470.4 233.6 457.6 220.8 457.6 208zM128 505.6C128 592 153.6 672 201.6 736c28.8-60.8 112-60.8 124.8-60.8-16-51.2 16-99.2 16-99.2l316.8-422.4c-48-19.2-99.2-32-150.4-32C297.6 118.4 128 291.2 128 505.6zM764.8 86.4c-22.4 19.2-390.4 518.4-390.4 518.4-22.4 28.8-12.8 76.8 22.4 99.2l9.6 6.4c35.2 22.4 80 12.8 99.2-25.6 0 0 6.4-12.8 9.6-19.2 54.4-105.6 275.2-524.8 288-553.6 6.4-19.2-3.2-32-19.2-32C777.6 76.8 771.2 80 764.8 86.4z"})]);Qc.displayName="OutlookIcon";var Yc=M({name:"AppearanceSwitch",setup(){const{config:e,status:t}=pn(),l=()=>{e.value==="switch"?t.value={light:"dark",dark:"auto",auto:"light"}[t.value]:t.value=t.value==="light"?"dark":"light"};return()=>s("button",{type:"button",id:"appearance-switch",onClick:()=>l()},[s(Wc,{style:{display:t.value==="auto"?"block":"none"}}),s(Uc,{style:{display:t.value==="dark"?"block":"none"}}),s(Gc,{style:{display:t.value==="light"?"block":"none"}})])}}),hf=M({name:"AppearanceMode",setup(){const e=ie(),{canToggle:t}=pn(),l=E(()=>e.value.outlookLocales.darkmode);return()=>t.value?s("div",{class:"appearance-wrapper"},[s("label",{class:"appearance-title",for:"appearance-switch"},l.value),s(Yc)]):null}});const Ia="VUEPRESS_THEME_COLOR";var vf=M({name:"ThemeColorPicker",props:{themeColor:{type:Object,required:!0}},setup(e){const t=(l="")=>{const n=document.documentElement.classList,a=gt(e.themeColor);if(!l){localStorage.removeItem(Ia),n.remove(...a);return}n.remove(...a.filter(i=>i!==l)),n.add(l),localStorage.setItem(Ia,l)};return ke(()=>{const l=localStorage.getItem(Ia);l&&t(l)}),()=>s("ul",{id:"theme-color-picker"},[s("li",s("span",{class:"theme-color",onClick:()=>t()})),sn(e.themeColor).map(([l,n])=>s("li",s("span",{style:{background:n},onClick:()=>t(l)})))])}});const hl=$i.enableThemeColor==="true",ff=hl?Jh(sn($i).filter(([e])=>e.startsWith("theme-"))):{};var gf=M({name:"ThemeColor",setup(){const e=ie(),t=E(()=>e.value.outlookLocales.themeColor);return()=>hl?s("div",{class:"theme-color-wrapper"},[s("label",{class:"theme-color-title",for:"theme-color-picker"},t.value),s(vf,{themeColor:ff})]):null}}),Xc=M({name:"ToggleFullScreenButton",setup(){const e=ie(),{isSupported:t,isFullscreen:l,toggle:n}=Si(),a=E(()=>e.value.outlookLocales.fullscreen);return()=>t?s("div",{class:"full-screen-wrapper"},[s("label",{class:"full-screen-title",for:"full-screen-switch"},a.value),s("button",{type:"button",id:"full-screen-switch",class:"full-screen",ariaPressed:l.value,onClick:()=>n()},l.value?s(Jc):s(Kc))]):null}}),Zc=M({name:"OutlookSettings",setup(){const e=el(),t=Il(),l=E(()=>!t.value&&e.value.fullscreen);return()=>s(Zn,()=>[hl?s(gf):null,s(hf),l.value?s(Xc):null])}}),mf=M({name:"NavScreen",props:{show:Boolean},emits:["close"],slots:Object,setup(e,{emit:t,slots:l}){const n=ue(),{isMobile:a}=dn(),i=Ue(),r=_c(i);return ke(()=>{i.value=document.body,ce(a,o=>{!o&&e.show&&(r.value=!1,t("close"))}),ce(()=>n.value.path,()=>{r.value=!1,t("close")})}),an(()=>{r.value=!1}),()=>s(Bt,{name:"fade",onEnter:()=>{r.value=!0},onAfterLeave:()=>{r.value=!1}},()=>{var o,c;return e.show?s("div",{id:"nav-screen"},s("div",{class:"vp-nav-screen-container"},[(o=l.before)==null?void 0:o.call(l),s(pf),s("div",{class:"vp-outlook-wrapper"},s(Zc)),(c=l.after)==null?void 0:c.call(l)])):null})}}),yf=M({name:"NavbarBrand",setup(){const e=mt(),t=rn(),l=ie(),n=E(()=>l.value.home||e.value),a=E(()=>t.value.title),i=E(()=>l.value.navTitle??a.value),r=E(()=>l.value.logo?Le(l.value.logo):null),o=E(()=>l.value.logoDark?Le(l.value.logoDark):null);return()=>s(Re,{to:n.value,class:"vp-brand"},()=>[r.value?s("img",{class:["vp-nav-logo",{light:!!o.value}],src:r.value,alt:a.value}):null,o.value?s("img",{class:["vp-nav-logo dark"],src:o.value,alt:a.value}):null,i.value?s("span",{class:["vp-site-name",{"hide-in-pad":r.value&&l.value.hideSiteNameOnMobile!==!1}]},i.value):null])}}),bf=M({name:"NavbarLinks",setup(){const e=qc();return()=>e.value.length?s("nav",{class:"vp-nav-links"},e.value.map(t=>s("div",{class:"nav-item hide-in-mobile"},"children"in t?s(cf,{config:t}):s(He,{config:t})))):null}}),_f=M({name:"RepoLink",components:{BitbucketIcon:uc,GiteeIcon:cc,GitHubIcon:sc,GitLabIcon:oc,SourceIcon:dc},setup(){const e=uf();return()=>e.value?s("div",{class:"nav-item vp-repo"},s("a",{class:"vp-repo-link",href:e.value.link,target:"_blank",rel:"noopener noreferrer","aria-label":e.value.label},s(Je(`${e.value.type}Icon`),{style:{width:"1.25rem",height:"1.25rem",verticalAlign:"middle"}}))):null}});const eu=({active:e=!1},{emit:t})=>s("button",{type:"button",class:["vp-toggle-navbar-button",{"is-active":e}],"aria-label":"Toggle Navbar","aria-expanded":e,"aria-controls":"nav-screen",onClick:()=>t("toggle")},s("span",[s("span",{class:"vp-top"}),s("span",{class:"vp-middle"}),s("span",{class:"vp-bottom"})]));eu.displayName="ToggleNavbarButton";var kf=eu;const Wa=(e,{emit:t})=>s("button",{type:"button",class:"vp-toggle-sidebar-button",title:"Toggle Sidebar",onClick:()=>t("toggle")},s("span",{class:"icon"}));Wa.displayName="ToggleSidebarButton",Wa.emits=["toggle"];var wf=Wa,Ef=M({name:"OutlookButton",setup(){const{isSupported:e}=Si(),t=el(),l=Il(),n=ue(),{canToggle:a}=pn(),i=W(!1),r=E(()=>!l.value&&t.value.fullscreen&&e);return ce(()=>n.value.path,()=>{i.value=!1}),()=>a.value||r.value||hl?s("div",{class:"nav-item hide-in-mobile"},a.value&&!r.value&&!hl?s(Yc):r.value&&!a.value&&!hl?s(Xc):s("button",{type:"button",class:["outlook-button",{open:i.value}],tabindex:"-1","aria-hidden":!0},[s(Qc),s("div",{class:"outlook-dropdown"},s(Zc))])):null}}),xf=M({name:"NavBar",emits:["toggleSidebar"],slots:Object,setup(e,{emit:t,slots:l}){const n=ie(),{isMobile:a}=dn(),i=W(!1),r=E(()=>{const{navbarAutoHide:d="mobile"}=n.value;return d!=="none"&&(d==="always"||a.value)}),o=E(()=>n.value.navbarLayout||{start:["Brand"],center:["Links"],end:["Language","Repo","Outlook","Search"]}),c={Brand:yf,Language:Ta,Links:bf,Repo:_f,Outlook:Ef,Search:ct("Docsearch")?Je("Docsearch"):ct("SearchBox")?Je("SearchBox"):Ta},u=d=>c[d]??(ct(d)?Je(d):Ta);return()=>{var d,p,h,v,b,w;return[s("header",{id:"navbar",class:["vp-navbar",{"auto-hide":r.value,"hide-icon":n.value.navbarIcon===!1}]},[s("div",{class:"vp-navbar-start"},[s(wf,{onToggle:()=>{i.value&&(i.value=!1),t("toggleSidebar")}}),(d=l.startBefore)==null?void 0:d.call(l),(o.value.start||[]).map(x=>s(u(x))),(p=l.startAfter)==null?void 0:p.call(l)]),s("div",{class:"vp-navbar-center"},[(h=l.centerBefore)==null?void 0:h.call(l),(o.value.center||[]).map(x=>s(u(x))),(v=l.centerAfter)==null?void 0:v.call(l)]),s("div",{class:"vp-navbar-end"},[(b=l.endBefore)==null?void 0:b.call(l),(o.value.end||[]).map(x=>s(u(x))),(w=l.endAfter)==null?void 0:w.call(l),s(kf,{active:i.value,onToggle:()=>{i.value=!i.value}})])]),s(mf,{show:i.value,onClose:()=>{i.value=!1}},{before:()=>{var x;return(x=l.screenTop)==null?void 0:x.call(l)},after:()=>{var x;return(x=l.screenBottom)==null?void 0:x.call(l)}})]}}}),Lf=M({name:"SidebarChild",props:{config:{type:Object,required:!0}},setup(e){const t=yt();return()=>[Nc(e.config,{class:["vp-sidebar-link",`vp-sidebar-${e.config.type}`,{active:kl(t,e.config,!0)}],exact:!0}),jc(e.config.children)]}}),Tf=M({name:"SidebarGroup",props:{config:{type:Object,required:!0},open:{type:Boolean,required:!0}},emits:["toggle"],setup(e,{emit:t}){const l=yt(),n=E(()=>kl(l,e.config)),a=E(()=>kl(l,e.config,!0));return()=>{const{collapsible:i,children:r=[],icon:o,prefix:c,link:u,text:d}=e.config;return s("section",{class:"vp-sidebar-group"},[s(i?"button":"p",{class:["vp-sidebar-heading",{clickable:i||u,exact:a.value,active:n.value}],...i?{type:"button",onClick:()=>t("toggle"),onKeydown:p=>{p.key==="Enter"&&t("toggle")}}:{}},[s(Ne,{icon:o}),u?s(He,{class:"vp-sidebar-title",config:{text:d,link:u},noExternalLinkIcon:!0}):s("span",{class:"vp-sidebar-title"},d),i?s("span",{class:["vp-arrow",e.open?"down":"end"]}):null]),e.open||!i?s(tu,{key:c,config:r}):null])}}}),tu=M({name:"SidebarLinks",props:{config:{type:Array,required:!0}},setup(e){const t=yt(),l=W(-1),n=a=>{l.value=a===l.value?-1:a};return ce(()=>t.path,()=>{const a=e.config.findIndex(i=>Fc(t,i));l.value=a},{immediate:!0,flush:"post"}),()=>s("ul",{class:"vp-sidebar-links"},e.config.map((a,i)=>s("li",a.type==="group"?s(Tf,{config:a,open:i===l.value,onToggle:()=>n(i)}):s(Lf,{config:a}))))}}),Af=M({name:"SideBar",slots:Object,setup(e,{slots:t}){const l=yt(),n=ie(),a=Fi(),i=Ue();return ke(()=>{ce(()=>l.hash,r=>{const o=document.querySelector(`.vp-sidebar a.vp-sidebar-link[href="${l.path}${r}"]`);if(!o)return;const{top:c,height:u}=i.value.getBoundingClientRect(),{top:d,height:p}=o.getBoundingClientRect();dc+u&&o.scrollIntoView(!1)})}),()=>{var r,o,c;return s("aside",{ref:i,id:"sidebar",class:["vp-sidebar",{"hide-icon":n.value.sidebarIcon===!1}]},[(r=t.top)==null?void 0:r.call(t),((o=t.default)==null?void 0:o.call(t))||s(tu,{config:a.value}),(c=t.bottom)==null?void 0:c.call(t)])}}}),Ni=M({name:"CommonWrapper",props:{containerClass:{type:String,default:""},noNavbar:Boolean,noSidebar:Boolean,noToc:Boolean},slots:Object,setup(e,{slots:t}){const l=Ve(),n=ue(),a=Ee(),i=ie(),{isMobile:r,isPC:o}=dn(),[c,u]=ls(!1),[d,p]=ls(!1),h=Fi(),v=W(!1),b=E(()=>e.noNavbar||a.value.navbar===!1||i.value.navbar===!1?!1:!!(n.value.title||i.value.logo||i.value.repo||i.value.navbar)),w=E(()=>e.noSidebar?!1:a.value.sidebar!==!1&&h.value.length!==0&&!a.value.home),x=E(()=>e.noToc||a.value.home?!1:a.value.toc||i.value.toc!==!1&&a.value.toc!==!1),g={x:0,y:0},_=R=>{g.x=R.changedTouches[0].clientX,g.y=R.changedTouches[0].clientY},P=R=>{const V=R.changedTouches[0].clientX-g.x,T=R.changedTouches[0].clientY-g.y;Math.abs(V)>Math.abs(T)*1.5&&Math.abs(V)>40&&(V>0&&g.x<=80?u(!0):u(!1))},C=()=>window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;let B=0;return Me("scroll",v1(()=>{const R=C();R<=58||R{R||u(!1)}),ke(()=>{const R=_c(document.body);ce(c,T=>{R.value=T});const V=l.afterEach(()=>{u(!1)});an(()=>{R.value=!1,V()})}),()=>s(ct("GlobalEncrypt")?Je("GlobalEncrypt"):Xo,()=>s("div",{class:["theme-container",{"no-navbar":!b.value,"no-sidebar":!w.value&&!(t.sidebar||t.sidebarTop||t.sidebarBottom),"has-toc":x.value,"hide-navbar":v.value,"sidebar-collapsed":!r.value&&!o.value&&d.value,"sidebar-open":r.value&&c.value},e.containerClass,a.value.containerClass||""],onTouchStart:_,onTouchEnd:P},[b.value?s(xf,{onToggleSidebar:()=>u()},{startBefore:()=>{var R;return(R=t.navbarStartBefore)==null?void 0:R.call(t)},startAfter:()=>{var R;return(R=t.navbarStartAfter)==null?void 0:R.call(t)},centerBefore:()=>{var R;return(R=t.navbarCenterBefore)==null?void 0:R.call(t)},centerAfter:()=>{var R;return(R=t.navbarCenterAfter)==null?void 0:R.call(t)},endBefore:()=>{var R;return(R=t.navbarEndBefore)==null?void 0:R.call(t)},endAfter:()=>{var R;return(R=t.navbarEndAfter)==null?void 0:R.call(t)},screenTop:()=>{var R;return(R=t.navScreenTop)==null?void 0:R.call(t)},screenBottom:()=>{var R;return(R=t.navScreenBottom)==null?void 0:R.call(t)}}):null,s(Bt,{name:"fade"},()=>c.value?s("div",{class:"vp-sidebar-mask",onClick:()=>u(!1)}):null),s(Bt,{name:"fade"},()=>r.value?null:s("div",{class:"toggle-sidebar-wrapper",onClick:()=>p()},s("span",{class:["arrow",d.value?"end":"start"]}))),s(Af,{},{...t.sidebar?{default:()=>t.sidebar()}:{},top:()=>{var R;return(R=t.sidebarTop)==null?void 0:R.call(t)},bottom:()=>{var R;return(R=t.sidebarBottom)==null?void 0:R.call(t)}}),t.default(),s(of)]))}}),pe=M({name:"DropTransition",props:{type:{type:String,default:"single"},delay:{type:Number,default:0},duration:{type:Number,default:.25},appear:Boolean},slots:Object,setup(e,{slots:t}){const l=a=>{a.style.transition=`transform ${e.duration}s ease-in-out ${e.delay}s, opacity ${e.duration}s ease-in-out ${e.delay}s`,a.style.transform="translateY(-20px)",a.style.opacity="0"},n=a=>{a.style.transform="translateY(0)",a.style.opacity="1"};return()=>s(e.type==="single"?Bt:h0,{name:"drop",appear:e.appear,onAppear:l,onAfterAppear:n,onEnter:l,onAfterEnter:n,onBeforeLeave:l},()=>t.default())}});const Ka=({custom:e})=>s(No,{class:["theme-hope-content",{custom:e}]});Ka.displayName="MarkdownContent",Ka.props={custom:Boolean};var ji=Ka;const lu=()=>s(de,{name:"author"},()=>s("path",{d:"M649.6 633.6c86.4-48 147.2-144 147.2-249.6 0-160-128-288-288-288s-288 128-288 288c0 108.8 57.6 201.6 147.2 249.6-121.6 48-214.4 153.6-240 288-3.2 9.6 0 19.2 6.4 25.6 3.2 9.6 12.8 12.8 22.4 12.8h704c9.6 0 19.2-3.2 25.6-12.8 6.4-6.4 9.6-16 6.4-25.6-25.6-134.4-121.6-240-243.2-288z"}));lu.displayName="AuthorIcon";const nu=()=>s(de,{name:"calendar"},()=>s("path",{d:"M716.4 110.137c0-18.753-14.72-33.473-33.472-33.473-18.753 0-33.473 14.72-33.473 33.473v33.473h66.993v-33.473zm-334.87 0c0-18.753-14.72-33.473-33.473-33.473s-33.52 14.72-33.52 33.473v33.473h66.993v-33.473zm468.81 33.52H716.4v100.465c0 18.753-14.72 33.473-33.472 33.473a33.145 33.145 0 01-33.473-33.473V143.657H381.53v100.465c0 18.753-14.72 33.473-33.473 33.473a33.145 33.145 0 01-33.473-33.473V143.657H180.6A134.314 134.314 0 0046.66 277.595v535.756A134.314 134.314 0 00180.6 947.289h669.74a134.36 134.36 0 00133.94-133.938V277.595a134.314 134.314 0 00-133.94-133.938zm33.473 267.877H147.126a33.145 33.145 0 01-33.473-33.473c0-18.752 14.72-33.473 33.473-33.473h736.687c18.752 0 33.472 14.72 33.472 33.473a33.145 33.145 0 01-33.472 33.473z"}));nu.displayName="CalendarIcon";const au=()=>s(de,{name:"category"},()=>s("path",{d:"M148.41 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H148.41c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.311-40.31zM147.556 553.478H429.73c22.263 0 40.311 18.048 40.311 40.31v282.176c0 22.263-18.048 40.312-40.31 40.312H147.555c-22.263 0-40.311-18.049-40.311-40.312V593.79c0-22.263 18.048-40.311 40.31-40.311zM593.927 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H593.927c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.31-40.31zM730.22 920.502H623.926c-40.925 0-74.22-33.388-74.22-74.425V623.992c0-41.038 33.387-74.424 74.425-74.424h222.085c41.038 0 74.424 33.226 74.424 74.067v114.233c0 10.244-8.304 18.548-18.547 18.548s-18.548-8.304-18.548-18.548V623.635c0-20.388-16.746-36.974-37.33-36.974H624.13c-20.585 0-37.331 16.747-37.331 37.33v222.086c0 20.585 16.654 37.331 37.126 37.331H730.22c10.243 0 18.547 8.304 18.547 18.547 0 10.244-8.304 18.547-18.547 18.547z"}));au.displayName="CategoryIcon";const iu=()=>s(de,{name:"print"},()=>s("path",{d:"M819.2 364.8h-44.8V128c0-17.067-14.933-32-32-32H281.6c-17.067 0-32 14.933-32 32v236.8h-44.8C145.067 364.8 96 413.867 96 473.6v192c0 59.733 49.067 108.8 108.8 108.8h44.8V896c0 17.067 14.933 32 32 32h460.8c17.067 0 32-14.933 32-32V774.4h44.8c59.733 0 108.8-49.067 108.8-108.8v-192c0-59.733-49.067-108.8-108.8-108.8zM313.6 160h396.8v204.8H313.6V160zm396.8 704H313.6V620.8h396.8V864zM864 665.6c0 25.6-19.2 44.8-44.8 44.8h-44.8V588.8c0-17.067-14.933-32-32-32H281.6c-17.067 0-32 14.933-32 32v121.6h-44.8c-25.6 0-44.8-19.2-44.8-44.8v-192c0-25.6 19.2-44.8 44.8-44.8h614.4c25.6 0 44.8 19.2 44.8 44.8v192z"}));iu.displayName="PrintIcon";const ru=()=>s(de,{name:"tag"},()=>s("path",{d:"M939.902 458.563L910.17 144.567c-1.507-16.272-14.465-29.13-30.737-30.737L565.438 84.098h-.402c-3.215 0-5.726 1.005-7.634 2.913l-470.39 470.39a10.004 10.004 0 000 14.164l365.423 365.424c1.909 1.908 4.42 2.913 7.132 2.913s5.223-1.005 7.132-2.913l470.39-470.39c2.01-2.11 3.014-5.023 2.813-8.036zm-240.067-72.121c-35.458 0-64.286-28.828-64.286-64.286s28.828-64.285 64.286-64.285 64.286 28.828 64.286 64.285-28.829 64.286-64.286 64.286z"}));ru.displayName="TagIcon";const su=()=>s(de,{name:"timer"},()=>s("path",{d:"M799.387 122.15c4.402-2.978 7.38-7.897 7.38-13.463v-1.165c0-8.933-7.38-16.312-16.312-16.312H256.33c-8.933 0-16.311 7.38-16.311 16.312v1.165c0 5.825 2.977 10.874 7.637 13.592 4.143 194.44 97.22 354.963 220.201 392.763-122.204 37.542-214.893 196.511-220.2 389.397-4.661 5.049-7.638 11.651-7.638 19.03v5.825h566.49v-5.825c0-7.379-2.849-13.981-7.509-18.9-5.049-193.016-97.867-351.985-220.2-389.527 123.24-37.67 216.446-198.453 220.588-392.892zM531.16 450.445v352.632c117.674 1.553 211.787 40.778 211.787 88.676H304.097c0-48.286 95.149-87.382 213.728-88.676V450.445c-93.077-3.107-167.901-81.297-167.901-177.093 0-8.803 6.99-15.793 15.793-15.793 8.803 0 15.794 6.99 15.794 15.793 0 80.261 63.69 145.635 142.01 145.635s142.011-65.374 142.011-145.635c0-8.803 6.99-15.793 15.794-15.793s15.793 6.99 15.793 15.793c0 95.019-73.789 172.82-165.96 177.093z"}));su.displayName="TimerIcon";const ou=()=>s(de,{name:"word"},()=>[s("path",{d:"M518.217 432.64V73.143A73.143 73.143 0 01603.43 1.097a512 512 0 01419.474 419.474 73.143 73.143 0 01-72.046 85.212H591.36a73.143 73.143 0 01-73.143-73.143z"}),s("path",{d:"M493.714 566.857h340.297a73.143 73.143 0 0173.143 85.577A457.143 457.143 0 11371.566 117.76a73.143 73.143 0 0185.577 73.143v339.383a36.571 36.571 0 0036.571 36.571z"})]);ou.displayName="WordIcon";const Ht=()=>{const e=ie();return E(()=>e.value.metaLocales)};var If=M({name:"AuthorInfo",inheritAttrs:!1,props:{author:{type:Array,required:!0},pure:Boolean},setup(e){const t=Ht();return()=>e.author.length?s("span",{class:"page-author-info","aria-label":`${t.value.author}${e.pure?"":"🖊"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(lu),s("span",e.author.map(l=>l.url?s("a",{class:"page-author-item",href:l.url,target:"_blank",rel:"noopener noreferrer"},l.name):s("span",{class:"page-author-item"},l.name))),s("span",{property:"author",content:e.author.map(l=>l.name).join(", ")})]):null}}),Pf=M({name:"CategoryInfo",inheritAttrs:!1,props:{category:{type:Array,required:!0},pure:Boolean},setup(e){const t=Ve(),l=ue(),n=Ht(),a=(i,r="")=>{r&&l.value.path!==r&&(i.preventDefault(),t.push(r))};return()=>e.category.length?s("span",{class:"page-category-info","aria-label":`${n.value.category}${e.pure?"":"🌈"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(au),e.category.map(({name:i,path:r})=>s("span",{class:["page-category-item",{[`category${la(i,9)}`]:!e.pure,clickable:r}],role:r?"navigation":"",onClick:o=>a(o,r)},i)),s("meta",{property:"articleSection",content:e.category.map(({name:i})=>i).join(",")})]):null}}),Of=M({name:"DateInfo",inheritAttrs:!1,props:{date:{type:Object,default:null},localizedDate:{type:String,default:""},pure:Boolean},setup(e){const t=$o(),l=Ht();return()=>e.date?s("span",{class:"page-date-info","aria-label":`${l.value.date}${e.pure?"":"📅"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(nu),s("span",s(Zn,()=>e.localizedDate||e.date.toLocaleDateString(t.value))),s("meta",{property:"datePublished",content:e.date.toISOString()||""})]):null}}),Cf=M({name:"OriginalInfo",inheritAttrs:!1,props:{isOriginal:Boolean},setup(e){const t=Ht();return()=>e.isOriginal?s("span",{class:"page-original-info"},t.value.origin):null}}),Rf=M({name:"ReadingTimeInfo",inheritAttrs:!1,props:{readingTime:{type:Object,default:()=>null},readingTimeLocale:{type:Object,default:()=>null},pure:Boolean},setup(e){const t=Ht(),l=E(()=>{if(!e.readingTime)return null;const{minutes:n}=e.readingTime;return n<1?"PT1M":`PT${Math.round(n)}M`});return()=>{var n,a;return(n=e.readingTimeLocale)!=null&&n.time?s("span",{class:"page-reading-time-info","aria-label":`${t.value.readingTime}${e.pure?"":"⌛"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(su),s("span",(a=e.readingTimeLocale)==null?void 0:a.time),s("meta",{property:"timeRequired",content:l.value})]):null}}}),Sf=M({name:"TagInfo",inheritAttrs:!1,props:{tag:{type:Array,default:()=>[]},pure:Boolean},setup(e){const t=Ve(),l=ue(),n=Ht(),a=(i,r="")=>{r&&l.value.path!==r&&(i.preventDefault(),t.push(r))};return()=>e.tag.length?s("span",{class:"page-tag-info","aria-label":`${n.value.tag}${e.pure?"":"🏷"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(ru),e.tag.map(({name:i,path:r})=>s("span",{class:["page-tag-item",{[`tag${la(i,9)}`]:!e.pure,clickable:r}],role:r?"navigation":"",onClick:o=>a(o,r)},i)),s("meta",{property:"keywords",content:e.tag.map(({name:i})=>i).join(",")})]):null}}),Df=M({name:"ReadTimeInfo",inheritAttrs:!1,props:{readingTime:{type:Object,default:()=>null},readingTimeLocale:{type:Object,default:()=>null},pure:Boolean},setup(e){const t=Ht();return()=>{var l,n,a;return(l=e.readingTimeLocale)!=null&&l.words?s("span",{class:"page-word-info","aria-label":`${t.value.words}${e.pure?"":"🔠"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(ou),s("span",(n=e.readingTimeLocale)==null?void 0:n.words),s("meta",{property:"wordCount",content:(a=e.readingTime)==null?void 0:a.words})]):null}}}),cu=M({name:"PageInfo",components:{AuthorInfo:If,CategoryInfo:Pf,DateInfo:Of,OriginalInfo:Cf,PageViewInfo:()=>null,ReadingTimeInfo:Rf,TagInfo:Sf,WordInfo:Df},props:{items:{type:[Array,Boolean],default:()=>["Author","Original","Date","PageView","ReadingTime","Category","Tag"]},info:{type:Object,required:!0}},setup(e){const t=Il();return()=>e.items?s("div",{class:"page-info"},e.items.map(l=>s(Je(`${l}Info`),{...e.info,pure:t.value}))):null}}),$f=M({name:"PrintButton",setup(){const e=el(),t=ie();return()=>e.value.print===!1?null:s("button",{type:"button",class:"print-button",title:t.value.metaLocales.print,onClick:()=>{window.print()}},s(iu))}});const Mf=({title:e,level:t,slug:l})=>s(Re,{to:`#${l}`,class:["toc-link",`level${t}`]},()=>e),Ja=(e,t)=>{const l=yt();return e.length&&t>0?s("ul",{class:"toc-list"},e.map(n=>{const a=Ja(n.children,t-1);return[s("li",{class:["toc-item",{active:Ci(l,`#${n.slug}`)}]},Mf(n)),a?s("li",a):null]})):null};var uu=M({name:"TOC",props:{items:{type:Array,default:()=>[]},headerDepth:{type:Number,default:2}},slots:Object,setup(e,{slots:t}){const l=yt(),n=ue(),a=Ht(),i=Ue(),r=W("-1.7rem"),o=u=>{var d;(d=i.value)==null||d.scrollTo({top:u,behavior:"smooth"})},c=()=>{if(i.value){const u=document.querySelector(".toc-item.active");u?r.value=`${u.getBoundingClientRect().top-i.value.getBoundingClientRect().top+i.value.scrollTop}px`:r.value="-1.7rem"}else r.value="-1.7rem"};return ke(()=>{ce(()=>l.hash,u=>{if(i.value){const d=document.querySelector(`#toc a.toc-link[href$="${u}"]`);if(!d)return;const{top:p,height:h}=i.value.getBoundingClientRect(),{top:v,height:b}=d.getBoundingClientRect();vp+h&&o(i.value.scrollTop+v+b-p-h)}}),ce(()=>l.fullPath,()=>c(),{flush:"post",immediate:!0})}),()=>{var u,d;const p=e.items.length?Ja(e.items,e.headerDepth):n.value.headers?Ja(n.value.headers,e.headerDepth):null;return p?s("div",{class:"toc-place-holder"},[s("aside",{id:"toc"},[(u=t.before)==null?void 0:u.call(t),s("div",{class:"toc-header"},[a.value.toc,s($f)]),s("div",{class:"toc-wrapper",ref:i},[p,s("div",{class:"toc-marker",style:{top:r.value}})]),(d=t.after)==null?void 0:d.call(t)])]):null}}}),Bi=M({name:"SkipLink",props:{content:{type:String,default:"main-content"}},setup(e){const t=ue(),l=ie(),n=Ue(),a=({target:i})=>{const r=document.querySelector(i.hash);if(r){const o=()=>{r.removeAttribute("tabindex"),r.removeEventListener("blur",o)};r.setAttribute("tabindex","-1"),r.addEventListener("blur",o),r.focus(),window.scrollTo(0,0)}};return ke(()=>{ce(()=>t.value.path,()=>n.value.focus())}),()=>[s("span",{ref:n,tabindex:"-1"}),s("a",{href:`#${e.content}`,class:"vp-skip-link sr-only",onClick:a},l.value.routeLocales.skipToContent)]}});let Pa=null,On=null;const Vf={wait:()=>Pa,pending:()=>{Pa=new Promise(e=>On=e)},resolve:()=>{On==null||On(),Pa=null,On=null}},du=()=>Vf;var Ff=M({name:"FadeSlideY",slots:Object,setup(e,{slots:t}){const{resolve:l,pending:n}=du();return()=>s(Bt,{name:"fade-slide-y",mode:"out-in",onBeforeEnter:l,onBeforeLeave:n},()=>{var a;return(a=t.default)==null?void 0:a.call(t)})}});const Nf=(e,t)=>{const l=e.replace(t,"/").split("/"),n=[];let a=Li(t);return l.forEach((i,r)=>{r!==l.length-1?(a+=`${i}/`,n.push({link:a,name:i||"Home"})):i!==""&&(a+=i,n.push({link:a,name:i}))}),n},pu=(e,{slots:t})=>{var l,n;const{bgImage:a,bgImageDark:i,bgImageStyle:r,color:o,description:c,image:u,imageDark:d,header:p,features:h=[]}=e;return s("div",{class:"vp-feature-wrapper"},[a?s("div",{class:["vp-feature-bg",{light:i}],style:[{"background-image":`url(${a})`},r]}):null,i?s("div",{class:"vp-feature-bg dark",style:[{"background-image":`url(${i})`},r]}):null,s("div",{class:"vp-feature",style:o?{color:o}:{}},[((l=t.image)==null?void 0:l.call(t,e))||[u?s("img",{class:["vp-feature-image",{light:d}],src:Le(u),alt:p}):null,d?s("img",{class:"vp-feature-image dark",src:Le(d),alt:p}):null],((n=t.info)==null?void 0:n.call(t,e))||[p?s("h2",{class:"vp-feature-header"},p):null,c?s("p",{class:"vp-feature-description",innerHTML:c}):null],h.length?s("div",{class:"vp-features"},h.map(({icon:v,title:b,details:w,link:x})=>{const g=[s("h3",{class:"vp-feature-title"},[s(Ne,{icon:v}),s("span",{innerHTML:b})]),s("p",{class:"vp-feature-details",innerHTML:w})];return x?Bn(x)?s("a",{class:"vp-feature-item link",href:x,role:"navigation","aria-label":b,target:"_blank"},g):s(Re,{class:"vp-feature-item link",to:x,role:"navigation","aria-label":b},()=>g):s("div",{class:"vp-feature-item"},g)})):null])])};pu.displayName="FeaturePanel";var _s=pu,jf=M({name:"HeroInfo",slots:Object,setup(e,{slots:t}){const l=Ee(),n=rn(),a=E(()=>l.value.heroFullScreen??!1),i=E(()=>{const{heroText:u,tagline:d}=l.value;return{text:u??n.value.title??"Hello",tagline:d??n.value.description??"",isFullScreen:a.value}}),r=E(()=>{const{heroText:u,heroImage:d,heroImageDark:p,heroAlt:h,heroImageStyle:v}=l.value;return{image:d?Le(d):null,imageDark:p?Le(p):null,heroStyle:v,alt:h||u||"hero image",isFullScreen:a.value}}),o=E(()=>{const{bgImage:u,bgImageDark:d,bgImageStyle:p}=l.value;return{image:Tt(u)?Le(u):null,imageDark:Tt(d)?Le(d):null,bgStyle:p,isFullScreen:a.value}}),c=E(()=>l.value.actions??[]);return()=>{var u,d,p;return s("header",{class:["vp-hero-info-wrapper",{fullscreen:a.value}]},[((u=t.heroBg)==null?void 0:u.call(t,o.value))||[o.value.image?s("div",{class:["vp-hero-mask",{light:o.value.imageDark}],style:[{"background-image":`url(${o.value.image})`},o.value.bgStyle]}):null,o.value.imageDark?s("div",{class:"vp-hero-mask dark",style:[{"background-image":`url(${o.value.imageDark})`},o.value.bgStyle]}):null],s("div",{class:"vp-hero-info"},[((d=t.heroImage)==null?void 0:d.call(t,r.value))||s(pe,{appear:!0,type:"group"},()=>[r.value.image?s("img",{key:"light",class:["vp-hero-image",{light:r.value.imageDark}],style:r.value.heroStyle,src:r.value.image,alt:r.value.alt}):null,r.value.imageDark?s("img",{key:"dark",class:"vp-hero-image dark",style:r.value.heroStyle,src:r.value.imageDark,alt:r.value.alt}):null]),((p=t.heroInfo)==null?void 0:p.call(t,i.value))??s("div",{class:"vp-hero-infos"},[i.value.text?s(pe,{appear:!0,delay:.04},()=>s("h1",{id:"main-title"},i.value.text)):null,i.value.tagline?s(pe,{appear:!0,delay:.08},()=>s("p",{class:"vp-description",innerHTML:i.value.tagline})):null,c.value.length?s(pe,{appear:!0,delay:.12},()=>s("p",{class:"vp-actions"},c.value.map(h=>s(He,{class:["vp-action",h.type||"default"],config:h,noExternalLinkIcon:!0})))):null])])])}}});const hu=(e,{slots:t})=>{var l,n,a;const{bgImage:i,bgImageDark:r,bgImageStyle:o,color:c,description:u,image:d,imageDark:p,header:h,highlights:v=[],type:b="un-order"}=e;return s("div",{class:"vp-highlight-wrapper",style:c?{color:c}:{}},[i?s("div",{class:["vp-highlight-bg",{light:r}],style:[{"background-image":`url(${i})`},o]}):null,r?s("div",{class:"vp-highlight-bg dark",style:[{"background-image":`url(${r})`},o]}):null,s("div",{class:"vp-highlight"},[((l=t.image)==null?void 0:l.call(t,e))||[d?s("img",{class:["vp-highlight-image",{light:p}],src:Le(d),alt:h}):null,p?s("img",{class:"vp-highlight-image dark",src:Le(p),alt:h}):null],((n=t.info)==null?void 0:n.call(t,e))||[s("div",{class:"vp-highlight-info-wrapper"},s("div",{class:"vp-highlight-info"},[h?s("h2",{class:"vp-highlight-header",innerHTML:h}):null,u?s("p",{class:"vp-highlight-description",innerHTML:u}):null,((a=t.highlights)==null?void 0:a.call(t,v))||s(b==="order"?"ol":b==="no-order"?"dl":"ul",{class:"vp-highlights"},v.map(({icon:w,title:x,details:g,link:_})=>{const P=[s(b==="no-order"?"dt":"h3",{class:"vp-highlight-title"},[w?s(Ne,{class:"vp-highlight-icon",icon:w}):null,s("span",{innerHTML:x})]),g?s(b==="no-order"?"dd":"p",{class:"vp-highlight-details",innerHTML:g}):null];return s(b==="no-order"?"div":"li",{class:["vp-highlight-item-wrapper",{link:_}]},_?Gh(_)?s("a",{class:"vp-highlight-item link",href:_,role:"navigation","aria-label":x,target:"_blank"},P):s(Re,{class:"vp-highlight-item link",to:_,role:"navigation","aria-label":x},()=>P):s("div",{class:"vp-highlight-item"},P))}))]))]])])};hu.displayName="HighlightPanel";var Bf=hu,zf=M({name:"HomePage",slots:Object,setup(e,{slots:t}){const l=Il(),n=Ee(),a=E(()=>{const{features:r}=n.value;return X(r)?r:null}),i=E(()=>{const{highlights:r}=n.value;return X(r)?r:null});return()=>{var r,o,c,u;return s("main",{id:"main-content",class:["vp-project-home ",{pure:l.value}],"aria-labelledby":n.value.heroText===null?"":"main-title"},[(r=t.top)==null?void 0:r.call(t),s(jf),((o=i.value)==null?void 0:o.map(d=>"features"in d?s(_s,d):s(Bf,d)))||(a.value?s(pe,{appear:!0,delay:.24},()=>s(_s,{features:a.value})):null),(c=t.center)==null?void 0:c.call(t),s(pe,{appear:!0,delay:.32},()=>s(ji)),(u=t.bottom)==null?void 0:u.call(t)])}}}),Hf=M({name:"BreadCrumb",setup(){const e=Ve(),t=ue(),l=mt(),n=Ee(),a=ie(),i=Ue([]),r=E(()=>(n.value.breadcrumb||n.value.breadcrumb!==!1&&a.value.breadcrumb!==!1)&&i.value.length>1),o=E(()=>n.value.breadcrumbIcon||n.value.breadcrumbIcon!==!1&&a.value.breadcrumbIcon!==!1),c=()=>{const u=e.getRoutes(),d=Nf(t.value.path,l.value).map(({link:p,name:h})=>{const v=u.find(b=>b.path===p);if(v){const{meta:b,path:w}=_l(e,v.path);return{title:b[be.shortTitle]||b[be.title]||h,icon:b[be.icon],path:w}}return null}).filter(p=>p!==null);d.length>1&&(i.value=d)};return ke(()=>{c(),ce(()=>t.value.path,c)}),()=>s("nav",{class:["vp-breadcrumb",{disable:!r.value}]},r.value?s("ol",{vocab:"https://schema.org/",typeof:"BreadcrumbList"},i.value.map((u,d)=>s("li",{class:{"is-active":i.value.length-1===d},property:"itemListElement",typeof:"ListItem"},[s(Re,{to:u.path,property:"item",typeof:"WebPage"},()=>[o.value?s(Ne,{icon:u.icon}):null,s("span",{property:"name"},u.title||"Unknown")]),s("meta",{property:"position",content:d+1})]))):[])}});const ks=e=>{const t=Ve();return e===!1?!1:ae(e)?pl(t,e,!0):xi(e)?e:null},Qa=(e,t,l)=>{const n=e.findIndex(a=>a.link===t);if(n!==-1){const a=e[n+l];return a!=null&&a.link?a:null}for(const a of e)if(a.children){const i=Qa(a.children,t,l);if(i)return i}return null};var qf=M({name:"PageNav",setup(){const e=ie(),t=Ee(),l=Fi(),n=ue(),a=un(),i=E(()=>{const o=ks(t.value.prev);return o===!1?null:o||(e.value.prevLink===!1?null:Qa(l.value,n.value.path,-1))}),r=E(()=>{const o=ks(t.value.next);return o===!1?null:o||(e.value.nextLink===!1?null:Qa(l.value,n.value.path,1))});return Me("keydown",o=>{o.altKey&&(o.key==="ArrowRight"?r.value&&(a(r.value.link),o.preventDefault()):o.key==="ArrowLeft"&&i.value&&(a(i.value.link),o.preventDefault()))}),()=>i.value||r.value?s("nav",{class:"vp-page-nav"},[i.value?s(He,{class:"prev",config:i.value},()=>{var o,c;return[s("div",{class:"hint"},[s("span",{class:"arrow start"}),e.value.metaLocales.prev]),s("div",{class:"link"},[s(Ne,{icon:(o=i.value)==null?void 0:o.icon}),(c=i.value)==null?void 0:c.text])]}):null,r.value?s(He,{class:"next",config:r.value},()=>{var o,c;return[s("div",{class:"hint"},[e.value.metaLocales.next,s("span",{class:"arrow end"})]),s("div",{class:"link"},[(o=r.value)==null?void 0:o.text,s(Ne,{icon:(c=r.value)==null?void 0:c.icon})])]}):null]):null}});const Uf={GitHub:":repo/edit/:branch/:path",GitLab:":repo/-/edit/:branch/:path",Gitee:":repo/edit/:branch/:path",Bitbucket:":repo/src/:branch/:path?mode=edit&spa=0&at=:branch&fileviewer=file-view-default"},Gf=({docsRepo:e,docsBranch:t,docsDir:l,filePathRelative:n,editLinkPattern:a})=>{if(!n)return null;const i=rc(e);let r;return a?r=a:i!==null&&(r=Uf[i]),r?r.replace(/:repo/,Zt(e)?e:`https://github.com/${e}`).replace(/:branch/,t).replace(/:path/,Po(`${Li(l)}/${n}`)):null},Wf=()=>{const e=ie(),t=ue(),l=Ee();return E(()=>{const{repo:n,docsRepo:a=n,docsBranch:i="main",docsDir:r="",editLink:o,editLinkPattern:c=""}=e.value;if(!(l.value.editLink??o??!0)||!a)return null;const u=Gf({docsRepo:a,docsBranch:i,docsDir:r,editLinkPattern:c,filePathRelative:t.value.filePathRelative});return u?{text:e.value.metaLocales.editLink,link:u}:null})},Kf=()=>{const e=rn(),t=ie(),l=ue(),n=Ee();return E(()=>{var a,i;return!(n.value.lastUpdated??t.value.lastUpdated??!0)||!((a=l.value.git)!=null&&a.updatedTime)?null:new Date((i=l.value.git)==null?void 0:i.updatedTime).toLocaleString(e.value.lang)})},Jf=()=>{const e=ie(),t=ue(),l=Ee();return E(()=>{var n;return l.value.contributors??e.value.contributors??!0?((n=t.value.git)==null?void 0:n.contributors)??null:null})};var Qf=M({name:"PageTitle",setup(){const e=ue(),t=Ee(),l=ie(),{info:n,items:a}=Xv();return()=>s("div",{class:"vp-page-title"},[s("h1",[l.value.titleIcon===!1?null:s(Ne,{icon:t.value.icon}),e.value.title]),s(cu,{info:n.value,...a.value===null?{}:{items:a.value}}),s("hr")])}});const vu=()=>s(de,{name:"edit"},()=>[s("path",{d:"M430.818 653.65a60.46 60.46 0 0 1-50.96-93.281l71.69-114.012 7.773-10.365L816.038 80.138A60.46 60.46 0 0 1 859.225 62a60.46 60.46 0 0 1 43.186 18.138l43.186 43.186a60.46 60.46 0 0 1 0 86.373L588.879 565.55l-8.637 8.637-117.466 68.234a60.46 60.46 0 0 1-31.958 11.229z"}),s("path",{d:"M728.802 962H252.891A190.883 190.883 0 0 1 62.008 771.98V296.934a190.883 190.883 0 0 1 190.883-192.61h267.754a60.46 60.46 0 0 1 0 120.92H252.891a69.962 69.962 0 0 0-69.098 69.099V771.98a69.962 69.962 0 0 0 69.098 69.098h475.911A69.962 69.962 0 0 0 797.9 771.98V503.363a60.46 60.46 0 1 1 120.922 0V771.98A190.883 190.883 0 0 1 728.802 962z"})]);vu.displayName="EditIcon";var Yf=M({name:"PageMeta",setup(){const e=ie(),t=Wf(),l=Kf(),n=Jf();return()=>{const{metaLocales:a}=e.value;return s("footer",{class:"page-meta"},[t.value?s("div",{class:"meta-item edit-link"},s(He,{class:"label",config:t.value},{before:()=>s(vu)})):null,s("div",{class:"meta-item git-info"},[l.value?s("div",{class:"update-time"},[s("span",{class:"label"},`${a.lastUpdated}: `),s(Zn,()=>s("span",{class:"info"},l.value))]):null,n.value&&n.value.length?s("div",{class:"contributors"},[s("span",{class:"label"},`${a.contributors}: `),n.value.map(({email:i,name:r},o)=>[s("span",{class:"contributor",title:`email: ${i}`},r),o!==n.value.length-1?",":""])]):null])])}}}),Xf=M({name:"NormalPage",slots:Object,setup(e,{slots:t}){const l=Ee(),n=ue(),{isDarkmode:a}=pn(),i=ie(),r=E(()=>l.value.toc||l.value.toc!==!1&&i.value.toc!==!1);return()=>s("main",{id:"main-content",class:"vp-page"},s(ct("LocalEncrypt")?Je("LocalEncrypt"):Xo,()=>{var o,c,u,d;return[(o=t.top)==null?void 0:o.call(t),l.value.cover?s("img",{class:"page-cover",src:Le(l.value.cover),alt:n.value.title,"no-view":""}):null,s(Hf),s(Qf),r.value?s(uu,{headerDepth:l.value.headerDepth??i.value.headerDepth??2},{before:()=>{var p;return(p=t.tocBefore)==null?void 0:p.call(t)},after:()=>{var p;return(p=t.tocAfter)==null?void 0:p.call(t)}}):null,(c=t.contentBefore)==null?void 0:c.call(t),s(ji),(u=t.contentAfter)==null?void 0:u.call(t),s(Yf),s(qf),ct("CommentService")?s(Je("CommentService"),{darkmode:a.value}):null,(d=t.bottom)==null?void 0:d.call(t)]}))}}),Zf=M({name:"Layout",setup(){const e=el(),t=ie(),l=ue(),n=Ee(),{isMobile:a}=dn(),i=E(()=>{var r,o;return((r=t.value.blog)==null?void 0:r.sidebarDisplay)||((o=e.value.blog)==null?void 0:o.sidebarDisplay)||"mobile"});return()=>[s(Bi),s(Ni,{},{default:()=>n.value.home?s(zf):s(Ff,()=>s(Xf,{key:l.value.path})),...i.value!=="none"?{navScreenBottom:()=>s(Je("BloggerInfo"))}:{},...!a.value&&i.value==="always"?{sidebar:()=>s(Je("BloggerInfo"))}:{}})]}}),e2=M({name:"NotFoundHint",setup(){const e=ie(),t=()=>{const l=e.value.routeLocales.notFoundMsg;return l[Math.floor(Math.random()*l.length)]};return()=>s("div",{class:"not-found-hint"},[s("p",{class:"error-code"},"404"),s("h1",{class:"error-title"},e.value.routeLocales.notFoundTitle),s("p",{class:"error-hint"},t())])}}),t2=M({name:"NotFound",slots:Object,setup(e,{slots:t}){const l=mt(),n=ie(),{navigate:a}=qa({to:n.value.home??l.value});return()=>[s(Bi),s(Ni,{noSidebar:!0},()=>{var i;return s("main",{id:"main-content",class:"vp-page not-found"},((i=t.default)==null?void 0:i.call(t))||[s(e2),s("div",{class:"actions"},[s("button",{type:"button",class:"action-button",onClick:()=>{window.history.go(-1)}},n.value.routeLocales.back),s("button",{type:"button",class:"action-button",onClick:()=>a()},n.value.routeLocales.home)])])})]}});const l2={GitHub:'',BiliBili:'',Email:'',Rss:''},n2={category:{"/":{path:"/category/",map:{}}},tag:{"/":{path:"/tag/",map:{Daily:{path:"/tag/daily/",keys:["v-4f919602","v-538a98d7","v-2f6ae09c","v-76f251d2","v-bd2b5fe8","v-75616b85","v-f5471cb0","v-5c48d497","v-fd7b7f92","v-b5a78a7a","v-81a71778","v-ca672354","v-7a74360a","v-1aafac08","v-f297935a","v-143a8bce","v-7e2c7a0c","v-79e139f8","v-f1efc11c","v-f47f129e","v-31eac486","v-30a50b00","v-0481cc80","v-1e305501","v-6dc18ec6","v-86a15cb6","v-57d9b582","v-2ba0b01e","v-d767e98e"]},Frontend:{path:"/tag/frontend/",keys:["v-30a50b00","v-1e305501","v-7320140c","v-72e84a92"]},Video:{path:"/tag/video/",keys:["v-5c48d497","v-ca672354","v-7a74360a","v-1aafac08","v-79e139f8","v-404740fa","v-f1efc11c","v-f47f129e","v-414fd2b2","v-30a50b00","v-0481cc80","v-1e305501"]},Linux:{path:"/tag/linux/",keys:["v-414fd2b2","v-31eac486"]},Docker:{path:"/tag/docker/",keys:["v-414fd2b2","v-31eac486"]},MySQL:{path:"/tag/mysql/",keys:["v-2f6ae09c","v-f297935a","v-143a8bce","v-7e2c7a0c","v-79e139f8"]},AI:{path:"/tag/ai/",keys:["v-81a71778","v-7a74360a","v-404740fa","v-221efd1f"]},Emotion:{path:"/tag/emotion/",keys:["v-f47f129e"]},"Working Experience":{path:"/tag/working-experience/",keys:["v-fd7b7f92","v-f47f129e"]},Tool:{path:"/tag/tool/",keys:["v-404740fa","v-17809471"]},Design:{path:"/tag/design/",keys:["v-bd2b5fe8"]},English:{path:"/tag/english/",keys:["v-221efd1f","v-6ed7d996","v-860c51c8","v-89760306","v-8cdfb444","v-d42db13c","v-ae153a4e","v-788d194a","v-58a409d7","v-3ac0474c","v-90496582","v-246f4f17","v-93b316c0","v-971cc7fe"]},Git:{path:"/tag/git/",keys:["v-60021cbc","v-071be141","v-4008ff77","v-48e494ef","v-0fbf5fdc","v-1e5872c4","v-642eaaea"]},GitLab:{path:"/tag/gitlab/",keys:["v-071be141","v-4008ff77"]},Java:{path:"/tag/java/",keys:["v-538a98d7","v-2f6ae09c","v-f5471cb0","v-b5a78a7a","v-ca672354","v-1aafac08","v-100d1814","v-65b23736","v-4008ff77","v-6dc18ec6","v-86a15cb6","v-57d9b582","v-2ba0b01e","v-d767e98e"]},"Node.js":{path:"/tag/node.js/",keys:["v-75616b85","v-0be1af08","v-4008ff77","v-c488ac58","v-efcacba2"]},Python:{path:"/tag/python/",keys:["v-0be1af08","v-071be141","v-5b37b3c6"]},JavaScript:{path:"/tag/javascript/",keys:["v-538a98d7"]},Testing:{path:"/tag/testing/",keys:["v-113531b4","v-65b23736","v-0be1af08","v-c488ac58","v-efcacba2"]}}}}},a2={article:{"/":{path:"/article/",keys:["v-386e94f1","v-0d6828c2","v-4f919602","v-538a98d7","v-2f6ae09c","v-76f251d2","v-bd2b5fe8","v-75616b85","v-f5471cb0","v-5c48d497","v-fd7b7f92","v-b5a78a7a","v-81a71778","v-ca672354","v-7a74360a","v-1aafac08","v-f297935a","v-143a8bce","v-7e2c7a0c","v-79e139f8","v-100d1814","v-60021cbc","v-404740fa","v-f1efc11c","v-f47f129e","v-414fd2b2","v-31eac486","v-30a50b00","v-0481cc80","v-1e305501","v-113531b4","v-65b23736","v-0be1af08","v-221efd1f","v-071be141","v-5b37b3c6","v-4008ff77","v-6dc18ec6","v-86a15cb6","v-57d9b582","v-2ba0b01e","v-6ed7d996","v-860c51c8","v-89760306","v-8cdfb444","v-d42db13c","v-ae153a4e","v-788d194a","v-58a409d7","v-3ac0474c","v-90496582","v-246f4f17","v-17809471","v-93b316c0","v-d767e98e","v-971cc7fe","v-48e494ef","v-0fbf5fdc","v-7320140c","v-c488ac58","v-1e5872c4","v-642eaaea","v-72e84a92","v-efcacba2"]}},star:{"/":{path:"/star/",keys:[]}},timeline:{"/":{path:"/timeline/",keys:["v-386e94f1","v-0d6828c2","v-4f919602","v-538a98d7","v-2f6ae09c","v-76f251d2","v-bd2b5fe8","v-75616b85","v-f5471cb0","v-5c48d497","v-fd7b7f92","v-b5a78a7a","v-81a71778","v-ca672354","v-7a74360a","v-1aafac08","v-f297935a","v-143a8bce","v-7e2c7a0c","v-79e139f8","v-100d1814","v-60021cbc","v-404740fa","v-f1efc11c","v-f47f129e","v-414fd2b2","v-31eac486","v-30a50b00","v-0481cc80","v-1e305501","v-113531b4","v-65b23736","v-0be1af08","v-221efd1f","v-071be141","v-5b37b3c6","v-4008ff77","v-6dc18ec6","v-86a15cb6","v-57d9b582","v-2ba0b01e","v-6ed7d996","v-860c51c8","v-89760306","v-8cdfb444","v-d42db13c","v-ae153a4e","v-788d194a","v-58a409d7","v-3ac0474c","v-90496582","v-246f4f17","v-17809471","v-93b316c0","v-d767e98e","v-971cc7fe","v-48e494ef","v-0fbf5fdc","v-7320140c","v-c488ac58","v-1e5872c4","v-642eaaea","v-72e84a92","v-efcacba2"]}}},ws=W(n2),fu=(e="")=>{const t=ue(),l=Ve(),n=mt();return E(()=>{var a;const i=e||((a=Ee().value.blog)==null?void 0:a.key)||"";if(!i)return console.warn("useBlogCategory: key not found"),{path:"/",map:{}};const r=l.getRoutes();if(!ws.value[i])throw new Error(`useBlogCategory: key ${i} is invalid`);const o=ws.value[i][n.value],c={path:o.path,map:{}};for(const u in o.map){const d=o.map[u];c.map[u]={path:d.path,items:[]};for(const p of d.keys){const h=r.find(({name:v})=>v===p);if(h){const v=_l(l,h.path);c.map[u].items.push({path:v.path,info:v.meta})}}t.value.path===d.path&&(c.currentItems=c.map[u].items)}return c})},Es=W(a2),ia=(e="")=>{const t=Ve(),l=mt();return E(()=>{var n;const a=e||((n=Ee().value.blog)==null?void 0:n.key)||"";if(!a)return console.warn("useBlogType: key not found"),{path:"/",items:[]};if(!Es.value[a])throw new Error(`useBlogType: key ${e} is invalid`);const i=t.getRoutes(),r=Es.value[a][l.value],o={path:r.path,items:[]};for(const c of r.keys){const u=i.find(({name:d})=>d===c);if(u){const d=_l(t,u.path);o.items.push({path:d.path,info:d.meta})}}return o})};const i2="/assets/hero-197a9d2d.jpg",gu=Symbol.for("categoryMap"),hn=()=>{const e=me(gu);if(!e)throw new Error("useCategoryMap() is called without provider.");return e},r2=()=>{const e=fu("category");ot(gu,e)},vn=()=>{const e=el(),t=ie();return E(()=>({...e.value.blog,...t.value.blog}))},mu=Symbol.for("tagMap"),fn=()=>{const e=me(mu);if(!e)throw new Error("useTagMap() is called without provider.");return e},s2=()=>{const e=fu("tag");ot(mu,e)},o2=e=>{const t=ie();return E(()=>{const{[be.author]:l}=e.value;return l?Yl(l):l===!1?[]:Yl(t.value.author,!1)})},c2=e=>{const t=hn();return E(()=>nc(e.value[be.category]).map(l=>({name:l,path:t.value.map[l].path})))},u2=e=>{const t=fn();return E(()=>ac(e.value[be.tag]).map(l=>({name:l,path:t.value.map[l].path})))},d2=e=>E(()=>{const{[be.date]:t}=e.value;return Pi(t)}),p2=e=>{const t=Ll(e,"info"),l=vn(),n=o2(t),a=c2(t),i=u2(t),r=d2(t),o=Sc(),c=E(()=>({author:n.value,category:a.value,date:r.value,localizedDate:t.value[be.localizedDate]||"",tag:i.value,isOriginal:t.value[be.isOriginal]||!1,readingTime:t.value[be.readingTime]||null,readingTimeLocale:t.value[be.readingTime]&&o.value?Rc(t.value[be.readingTime],o.value):null,pageview:e.path})),u=E(()=>l.value.articleInfo);return{info:c,items:u}},yu=Symbol(""),gn=()=>{const e=me(yu);if(!e)throw new Error("useArticles() is called without provider.");return e},h2=()=>{const e=ia("article");ot(yu,e)},bu=Symbol(""),zi=()=>{const e=me(bu);if(!e)throw new Error("useStars() is called without provider.");return e},v2=()=>{const e=ia("star");ot(bu,e)},_u=Symbol(""),Hi=()=>{const e=me(_u);if(!e)throw new Error("useTimelines() is called without provider.");return e},f2=()=>{const e=ia("timeline"),t=E(()=>{const l=[];return e.value.items.forEach(({info:n,path:a})=>{const i=Pi(n[be.date]),r=i==null?void 0:i.getFullYear(),o=i?i.getMonth()+1:null,c=i==null?void 0:i.getDate();r&&o&&c&&((!l[0]||l[0].year!==r)&&l.unshift({year:r,items:[]}),l[0].items.push({date:`${o}/${c}`,info:n,path:a}))}),{...e.value,config:l.reverse()}});ot(_u,t)},g2=()=>{h2(),r2(),v2(),s2(),f2()};var m2=M({name:"SocialMedia",setup(){const e=vn(),t=Il(),l=E(()=>{const n=e.value.medias;return n?sn(n).map(([a,i])=>({name:a,icon:l2[a],url:i})):[]});return()=>l.value.length?s("div",{class:"vp-social-medias"},l.value.map(({name:n,icon:a,url:i})=>s("a",{class:"vp-social-media",href:i,rel:"noopener noreferrer",target:"_blank","aria-label":n,...t.value?{}:{"data-balloon-pos":"up"},innerHTML:a}))):null}}),qi=M({name:"BloggerInfo",setup(){const e=vn(),t=rn(),l=ie(),n=gn(),a=hn(),i=fn(),r=Hi(),o=un(),c=E(()=>{var h;return e.value.name||((h=Yl(l.value.author)[0])==null?void 0:h.name)||t.value.title}),u=E(()=>e.value.avatar||l.value.logo),d=E(()=>l.value.blogLocales),p=E(()=>e.value.intro);return()=>{const{article:h,category:v,tag:b,timeline:w}=d.value,x=[[n.value.path,n.value.items.length,h],[a.value.path,gt(a.value.map).length,v],[i.value.path,gt(i.value.map).length,b],[r.value.path,r.value.items.length,w]];return s("div",{class:"vp-blogger-info",vocab:"https://schema.org/",typeof:"Person"},[s("div",{class:"vp-blogger",...p.value?{style:{cursor:"pointer"},"aria-label":d.value.intro,"data-balloon-pos":"down",role:"navigation",onClick:()=>o(p.value)}:{}},[u.value?s("img",{class:["vp-blogger-avatar",{round:e.value.roundAvatar}],src:Le(u.value),property:"image",alt:"Blogger Avatar"}):null,c.value?s("div",{class:"vp-blogger-name",property:"name"},c.value):null,e.value.description?s("div",{class:"vp-blogger-description",innerHTML:e.value.description}):null,p.value?s("meta",{property:"url",content:Le(p.value)}):null]),s("div",{class:"vp-blog-counts"},x.map(([g,_,P])=>s(Re,{class:"vp-blog-count",to:g},()=>[s("div",{class:"count"},_),s("div",P)]))),s(m2)])}}});const Ya=()=>s(de,{name:"category"},()=>s("path",{d:"M148.41 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H148.41c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.311-40.31zM147.556 553.478H429.73c22.263 0 40.311 18.048 40.311 40.31v282.176c0 22.263-18.048 40.312-40.31 40.312H147.555c-22.263 0-40.311-18.049-40.311-40.312V593.79c0-22.263 18.048-40.311 40.31-40.311zM593.927 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H593.927c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.31-40.31zM730.22 920.502H623.926c-40.925 0-74.22-33.388-74.22-74.425V623.992c0-41.038 33.387-74.424 74.425-74.424h222.085c41.038 0 74.424 33.226 74.424 74.067v114.233c0 10.244-8.304 18.548-18.547 18.548s-18.548-8.304-18.548-18.548V623.635c0-20.388-16.746-36.974-37.33-36.974H624.13c-20.585 0-37.331 16.747-37.331 37.33v222.086c0 20.585 16.654 37.331 37.126 37.331H730.22c10.243 0 18.547 8.304 18.547 18.547 0 10.244-8.304 18.547-18.547 18.547z"}));Ya.displayName="CategoryIcon";const Xa=()=>s(de,{name:"tag"},()=>s("path",{d:"M939.902 458.563L910.17 144.567c-1.507-16.272-14.465-29.13-30.737-30.737L565.438 84.098h-.402c-3.215 0-5.726 1.005-7.634 2.913l-470.39 470.39a10.004 10.004 0 000 14.164l365.423 365.424c1.909 1.908 4.42 2.913 7.132 2.913s5.223-1.005 7.132-2.913l470.39-470.39c2.01-2.11 3.014-5.023 2.813-8.036zm-240.067-72.121c-35.458 0-64.286-28.828-64.286-64.286s28.828-64.285 64.286-64.285 64.286 28.828 64.286 64.285-28.829 64.286-64.286 64.286z"}));Xa.displayName="TagIcon";const Ui=()=>s(de,{name:"timeline"},()=>s("path",{d:"M511.997 70.568c-243.797 0-441.429 197.633-441.429 441.435 0 243.797 197.632 441.429 441.43 441.429S953.431 755.8 953.431 512.002c0-243.796-197.637-441.434-441.435-441.434zm150.158 609.093-15.605 15.61c-8.621 8.615-22.596 8.615-31.215 0L472.197 552.126c-4.95-4.944-4.34-14.888-4.34-24.677V247.14c0-12.19 9.882-22.07 22.07-22.07h22.07c12.19 0 22.07 9.882 22.07 22.07v273.218l128.088 128.088c8.62 8.62 8.62 22.595 0 31.215zm0 0"}));Ui.displayName="TimelineIcon";const ku=()=>s(de,{name:"slides"},()=>s("path",{d:"M896 170.667v426.666a85.333 85.333 0 0 1-85.333 85.334h-256v61.184l192.597 115.584-43.861 73.13-148.736-89.173v95.275h-85.334v-95.318l-148.736 89.216-43.861-73.13 192.597-115.627v-61.141h-256A85.333 85.333 0 0 1 128 597.333V170.667H85.333V85.333h853.334v85.334H896zm-682.667 0v426.666h597.334V170.667H213.333zM426.667 512h-85.334V341.333h85.334V512zm128 0h-85.334V256h85.334v256zm128 0h-85.334V384h85.334v128z"}));ku.displayName="SlideIcon";const wu=()=>s(de,{name:"sticky"},()=>[s("path",{d:"m381.3 733.8l-161.9 118c-5.9 4.5-13.2 6.6-20.1 6.6-8.7 0-17.7-3.4-24.3-10-12.2-12.2-13.9-31.3-3.5-45.2l144.5-195.5-113.6-112.9c-11.1-11.1-13.2-28.4-5.5-42 5.5-8.7 52.1-76.4 155.5-51 1.8 0.3 3.5 0.3 5.6 0.7 4.2 0.3 9 0.7 14.2 1.7 21.9 3.5 60.8-13.9 94.5-42.7 32.3-27.5 53.1-59.4 53.1-81.6 0-5.2 0-10.8-0.3-16-0.7-20.8-2.1-52.8 21.5-76.4 28.1-28.1 72.9-30.6 103.9-5.2 0.6 0.3 1 1 1.7 1.7 16.7 16.3 187.5 187.2 189.3 188.9 14.5 14.6 22.9 34.4 22.9 55.3 0 20.8-8 40.2-22.9 54.8-23.7 23.6-56 22.6-77.1 21.6-4.9 0-10.5-0.4-15.7-0.4-20.8 0-45.8 14.6-70.5 41.3-34.3 37.5-55.5 85.8-53.8 107.7 0.7 6.9 2.1 19.1 2.4 20.8 25 101.4-42.7 147.6-50.7 152.8-13.9 8.4-31.6 6.3-42.7-4.8l-112.1-112.2z"})]);wu.displayName="StickyIcon";const qn=()=>s(de,{name:"article"},()=>s("path",{d:"M853.333 938.667H170.667A42.667 42.667 0 0 1 128 896V128a42.667 42.667 0 0 1 42.667-42.667h682.666A42.667 42.667 0 0 1 896 128v768a42.667 42.667 0 0 1-42.667 42.667zm-42.666-85.334V170.667H213.333v682.666h597.334zM298.667 256h170.666v170.667H298.667V256zm0 256h426.666v85.333H298.667V512zm0 170.667h426.666V768H298.667v-85.333zm256-384h170.666V384H554.667v-85.333z"}));qn.displayName="ArticleIcon";const Eu=()=>s(de,{name:"book"},()=>s("path",{d:"M256 853.333h426.667A85.333 85.333 0 0 0 768 768V256a85.333 85.333 0 0 0-85.333-85.333H469.333a42.667 42.667 0 0 1 0-85.334h213.334A170.667 170.667 0 0 1 853.333 256v512a170.667 170.667 0 0 1-170.666 170.667H213.333A42.667 42.667 0 0 1 170.667 896V128a42.667 42.667 0 0 1 42.666-42.667h128A42.667 42.667 0 0 1 384 128v304.256l61.653-41.088a42.667 42.667 0 0 1 47.36 0l61.654 41.045V256A42.667 42.667 0 0 1 640 256v256a42.667 42.667 0 0 1-66.347 35.499l-104.32-69.547-104.32 69.547A42.667 42.667 0 0 1 298.667 512V170.667H256v682.666z"}));Eu.displayName="BookIcon";const xu=()=>s(de,{name:"link"},()=>s("path",{d:"M460.8 584.533c17.067 17.067 17.067 42.667 0 59.734-17.067 17.066-42.667 17.066-59.733 0-85.334-85.334-85.334-217.6 0-302.934L554.667 192C640 110.933 776.533 110.933 857.6 196.267c81.067 81.066 81.067 213.333 0 294.4l-68.267 64c0-34.134-4.266-68.267-17.066-102.4l21.333-21.334c51.2-46.933 55.467-128 4.267-179.2s-128-55.466-179.2-4.266c-4.267 0-4.267 4.266-4.267 4.266L465.067 401.067c-51.2 51.2-51.2 132.266-4.267 183.466m123.733-183.466C601.6 384 627.2 384 644.267 401.067c85.333 85.333 85.333 217.6 0 302.933l-153.6 149.333C405.333 934.4 268.8 934.4 187.733 849.067c-81.066-81.067-81.066-213.334 0-294.4l68.267-64c0 34.133 4.267 72.533 17.067 102.4L251.733 614.4C204.8 665.6 204.8 746.667 256 793.6c51.2 46.933 123.733 46.933 174.933 0l149.334-149.333c51.2-51.2 51.2-128 0-179.2-12.8-17.067-17.067-46.934 4.266-64z"}));xu.displayName="LinkIcon";const Lu=()=>s(de,{name:"project"},()=>s("path",{d:"M987.456 425.152H864V295.296a36.48 36.48 0 0 0-36.544-36.544h-360l-134.08-128.256A9.344 9.344 0 0 0 327.04 128H36.48A36.48 36.48 0 0 0 0 164.544v676.608a36.48 36.48 0 0 0 36.544 36.544h797.76a36.672 36.672 0 0 0 33.92-22.848L1021.44 475.52a36.48 36.48 0 0 0-33.92-50.304zM82.304 210.304h215.424l136.64 130.752h347.328v84.096H198.848A36.672 36.672 0 0 0 164.928 448L82.304 652.8V210.304zM808.32 795.456H108.544l118.08-292.608h699.904L808.32 795.52z"}));Lu.displayName="ProjectIcon";const Tu=()=>s(de,{name:"friend"},()=>s("path",{d:"M860.16 213.333A268.373 268.373 0 0 0 512 186.027a267.52 267.52 0 0 0-348.16 404.48L428.8 855.893a118.613 118.613 0 0 0 166.4 0l264.96-265.386a267.52 267.52 0 0 0 0-377.174zM800 531.627l-264.96 264.96a32.427 32.427 0 0 1-46.08 0L224 530.347a183.04 183.04 0 0 1 0-256 182.187 182.187 0 0 1 256 0 42.667 42.667 0 0 0 60.587 0 182.187 182.187 0 0 1 256 0 183.04 183.04 0 0 1 3.413 256z"}));Tu.displayName="FriendIcon";const Za=()=>s(de,{name:"slide-down"},()=>s("path",{d:"M108.775 312.23c13.553 0 27.106 3.734 39.153 11.806l375.205 250.338 363.641-252.808c32.587-21.624 76.499-12.83 98.123 19.757 21.685 32.467 12.95 76.56-19.576 98.184l-402.854 278.89c-23.733 15.901-54.694 15.962-78.547.12L69.501 442.097c-32.647-21.685-41.441-65.777-19.817-98.304 13.734-20.54 36.201-31.563 59.09-31.563Z"}));Za.displayName="SlideDownIcon";const Au=()=>s("svg",{xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink",class:"empty-icon",viewBox:"0 0 1024 1024",innerHTML:''});Au.displayName="EmptyIcon";const Iu=()=>s(de,{name:"lock"},()=>s("path",{d:"M787.168 952.268H236.832c-30.395 0-55.033-24.638-55.033-55.033V429.45c0-30.395 24.638-55.034 55.033-55.034h82.55V264.35c0-106.38 86.238-192.618 192.618-192.618S704.618 157.97 704.618 264.35v110.066h82.55c30.395 0 55.033 24.639 55.033 55.034v467.785c0 30.395-24.639 55.033-55.033 55.033zM484.483 672.046v115.122h55.034V672.046c31.99-11.373 55.033-41.605 55.033-77.496 0-45.592-36.958-82.55-82.55-82.55s-82.55 36.958-82.55 82.55c0 35.89 23.042 66.123 55.033 77.496zM622.067 264.35c0-60.788-49.28-110.067-110.067-110.067s-110.067 49.28-110.067 110.067v110.066h220.135V264.35z"}));Iu.displayName="LockIcon";var y2=M({name:"ArticleItem",props:{info:{type:Object,required:!0},path:{type:String,required:!0}},slots:Object,setup(e,{slots:t}){const l=Ll(e,"info"),{info:n,items:a}=p2(e);return()=>{var i,r,o;const{[be.title]:c,[be.type]:u,[be.isEncrypted]:d=!1,[be.cover]:p,[be.excerpt]:h,[be.sticky]:v}=l.value,b=n.value;return s("div",{class:"vp-article-wrapper"},s("article",{class:"vp-article-item",vocab:"https://schema.org/",typeof:"Article"},[((i=t.cover)==null?void 0:i.call(t,{cover:p}))||(p?[s("img",{class:"vp-article-cover",src:Le(p)}),s("meta",{property:"image",content:Le(p)})]:[]),v?s(wu):null,s(Re,{to:e.path},()=>{var w;return((w=t.title)==null?void 0:w.call(t,{title:c,isEncrypted:d,type:u}))||s("header",{class:"vp-article-title"},[d?s(Iu):null,u===$c.slide?s(ku):null,s("span",{property:"headline"},c)])}),((r=t.excerpt)==null?void 0:r.call(t,{excerpt:h}))||(h?s("div",{class:"vp-article-excerpt",innerHTML:h}):null),s("hr",{class:"vp-article-hr"}),((o=t.info)==null?void 0:o.call(t,{info:b}))||s(cu,{info:b,...a.value?{items:a.value}:{}})]))}}}),b2=M({name:"Pagination",props:{total:{type:Number,default:10},perPage:{type:Number,default:10},current:{type:Number,default:1}},emits:["updateCurrentPage"],setup(e,{emit:t}){let l;const n=ie(),a=W(""),i=E(()=>n.value.paginationLocales),r=E(()=>Math.ceil(e.total/e.perPage)),o=E(()=>!!r.value&&r.value!==1),c=E(()=>r.value<7?!1:e.current>4),u=E(()=>r.value<7?!1:e.current{const{current:v}=e;let b=1,w=r.value;const x=[];r.value>=7&&(v<=4&&v4&&v>=r.value-3?(w=r.value,b=r.value-4):r.value>7&&(b=v-2,w=v+2));for(let g=b;g<=w;g++)x.push(g);return x}),p=v=>t("updateCurrentPage",v),h=v=>{const b=parseInt(v);b<=r.value&&b>0?p(b):l.pop(`${i.value.errorText.replace(/\$page/g,r.value.toString())}`)};return ke(()=>{l=new Yh}),()=>s("div",{class:"vp-pagination"},o.value?s("div",{class:"vp-pagination-list"},[s("div",{class:"vp-pagination-number "},[e.current>1?s("div",{class:"prev",role:"navigation",unselectable:"on",onClick:()=>p(e.current-1)},i.value.prev):null,c.value?[s("div",{role:"navigation",onClick:()=>p(1)},1),s("div",{class:"ellipsis"},"...")]:null,d.value.map(v=>s("div",{key:v,class:{active:e.current===v},role:"navigation",onClick:()=>p(v)},v)),u.value?[s("div",{class:"ellipsis"},"..."),s("div",{role:"navigation",onClick:()=>p(r.value)},r.value)]:null,e.currentp(e.current+1)},i.value.next):null]),s("div",{class:"vp-pagination-nav"},[s("label",{for:"navigation-text"},`${i.value.navigate}: `),s("input",{id:"navigation-text",value:a.value,onInput:({target:v})=>{a.value=v.value},onKeydown:v=>{v.key==="Enter"&&(v.preventDefault(),h(a.value))}}),s("button",{class:"vp-pagination-button",role:"navigation",title:i.value.action,onClick:()=>h(a.value)},i.value.action)])]):[])}}),Gi=M({name:"ArticleList",props:{items:{type:Array,default:()=>[]}},setup(e){const t=yt(),l=Ve(),n=vn(),a=W(1),i=E(()=>n.value.articlePerPage||10),r=E(()=>e.items.slice((a.value-1)*i.value,a.value*i.value)),o=c=>{a.value=c;const u={...t.query};u.page===c.toString()||c===1&&!u.page||(c===1?delete u.page:u.page=c.toString(),l.push({path:t.path,query:u}))};return ke(()=>{const{page:c}=t.query;o(c?Number(c):1),ce(a,()=>{const u=document.querySelector("#article-list").getBoundingClientRect().top+window.scrollY;setTimeout(()=>{window.scrollTo(0,u)},100)}),ce(()=>t.query,({page:u})=>{o(u?Number(u):1)})}),()=>s("div",{id:"article-list",class:"vp-article-list"},r.value.length?[...r.value.map(({info:c,path:u},d)=>s(pe,{appear:!0,delay:d*.04},()=>s(y2,{key:u,info:c,path:u}))),s(b2,{current:a.value,perPage:i.value,total:e.items.length,onUpdateCurrentPage:o})]:s(Au))}}),Wi=M({name:"CategoryList",setup(){const e=ue(),t=hn();return()=>s("ul",{class:"vp-category-list"},sn(t.value.map).map(([l,{path:n,items:a}])=>s("li",{class:["vp-category",`vp-category${la(l,9)}`,{active:n===e.value.path}]},s(Re,{to:n},()=>[l,s("span",{class:"count"},a.length)]))))}}),Ki=M({name:"TagList",setup(){const e=Ee(),t=fn(),l=n=>{var a;return n===((a=e.value.blog)==null?void 0:a.name)};return()=>s("ul",{class:"tag-list-wrapper"},sn(t.value.map).map(([n,{path:a,items:i}])=>s("li",{class:["tag",`tag${la(n,9)}`,{active:l(n)}]},s(Re,{to:a},()=>[n,s("span",{class:"tag-num"},i.length)]))))}}),_2=M({name:"TimelineList",setup(){const e=ie(),t=Hi(),l=un(),n=E(()=>e.value.blogLocales.timeline);return()=>s("div",{class:"timeline-list-wrapper"},[s("div",{class:"timeline-list-title",onClick:()=>l(t.value.path)},[s(Ui),s("span",{class:"num"},t.value.items.length),n.value]),s("hr"),s("div",{class:"timeline-content"},s("ul",{class:"timeline-list"},t.value.config.map(({year:a,items:i},r)=>s(pe,{appear:!0,delay:.08*(r+1)},()=>s("li",[s("h3",{class:"timeline-year"},a),s("ul",{class:"timeline-year-wrapper"},i.map(({date:o,info:c,path:u})=>s("li",{class:"timeline-item"},[s("span",{class:"timeline-date"},o),s(Re,{class:"timeline-title",to:u},()=>c[be.title])])))])))))])}}),Pu=M({name:"InfoList",setup(){const e=ie(),t=gn(),l=hn(),n=E(()=>gt(l.value.map).length),a=zi(),i=fn(),r=E(()=>gt(i.value.map).length),o=un(),c=W("article"),u=E(()=>e.value.blogLocales),d=[["article",qn],["category",Ya],["tag",Xa],["timeline",Ui]];return()=>s("div",{class:"vp-blog-infos"},[s("div",{class:"vp-blog-type-switcher"},d.map(([p,h])=>s("button",{type:"button",class:"vp-blog-type-button",onClick:()=>{c.value=p}},s("div",{class:["icon-wrapper",{active:c.value===p}],"aria-label":u.value[p],"data-balloon-pos":"up"},s(h))))),s(pe,()=>c.value==="article"?s("div",{class:"vp-sticky-article-wrapper"},[s("div",{class:"title",onClick:()=>o(t.value.path)},[s(qn),s("span",{class:"num"},t.value.items.length),u.value.article]),s("hr"),s("ul",{class:"vp-sticky-articles"},a.value.items.map(({info:p,path:h},v)=>s(pe,{appear:!0,delay:.08*(v+1)},()=>s("li",{class:"vp-sticky-article"},s(Re,{to:h},()=>p[be.title])))))]):c.value==="category"?s("div",{class:"vp-category-wrapper"},[n.value?s("div",{class:"title",onClick:()=>o(l.value.path)},[s(Ya),s("span",{class:"num"},n.value),u.value.category]):null,s("hr"),s(pe,{delay:.04},()=>s(Wi))]):c.value==="tag"?s("div",{class:"vp-tag-wrapper"},[r.value?s("div",{class:"title",onClick:()=>o(i.value.path)},[s(Xa),s("span",{class:"num"},r.value),u.value.tag]):null,s("hr"),s(pe,{delay:.04},()=>s(Ki))]):s(pe,()=>s(_2)))])}}),ra=M({name:"BlogWrapper",slots:Object,setup(e,{slots:t}){const{isMobile:l}=dn();return()=>[s(Bi),s(Ni,{noSidebar:!0,noToc:!0},{default:()=>t.default(),navScreenBottom:()=>s(qi),...l.value?{sidebar:()=>s(Pu)}:{}})]}});const Ou=()=>s("aside",{class:"vp-blog-info-wrapper"},[s(pe,()=>s(qi)),s(pe,{delay:.04},()=>s(Pu))]);Ou.displayName="InfoPanel";var sa=Ou,k2=M({name:"BlogPage",components:{CategoryList:Wi,TagList:Ki},setup(){const e=ue(),t=Ee(),l=hn(),n=fn(),a=E(()=>t.value.blog||{}),i=E(()=>{const{key:o=""}=a.value;return o==="category"?"CategoryList":o==="tag"?"TagList":null}),r=E(()=>{const{name:o="",key:c=""}=a.value;return c==="category"?o?l.value.map[o].items:[]:c==="tag"?o?n.value.map[o].items:[]:[]});return()=>s(ra,()=>s("div",{class:"vp-page vp-blog"},s("div",{class:"blog-page-wrapper"},[s("main",{id:"main-content",class:"vp-blog-main"},[s(pe,()=>i.value?s(Je(i.value)):null),a.value.name?s(pe,{appear:!0,delay:.24},()=>s(Gi,{key:e.value.path,items:r.value})):null]),s(pe,{delay:.16},()=>s(sa,{key:"blog"}))])))}}),w2=M({name:"BlogHero",slots:Object,setup(e,{slots:t}){const l=Ee(),n=rn(),a=Ue(),i=E(()=>l.value.heroFullScreen??!1),r=E(()=>{const{heroText:c,heroImage:u,heroImageDark:d,heroAlt:p,heroImageStyle:h,tagline:v}=l.value;return{text:c??n.value.title??"Hello",image:u?Le(u):null,imageDark:d?Le(d):null,heroStyle:h,alt:p||c||"hero image",tagline:v??"",isFullScreen:i.value}}),o=E(()=>{const{bgImage:c,bgImageDark:u,bgImageStyle:d}=l.value;return{image:ae(c)?Le(c):c===!1?null:i2,imageDark:ae(u)?Le(u):null,bgStyle:d,isFullScreen:i.value}});return()=>{var c,u;return l.value.hero===!1?null:s("div",{ref:a,class:["vp-blog-hero",{fullscreen:i.value,"no-bg":!o.value.image}]},[((c=t.heroBg)==null?void 0:c.call(t,o.value))||[o.value.image?s("div",{class:["vp-blog-mask",{light:o.value.imageDark}],style:[{background:`url(${o.value.image}) center/cover no-repeat`},o.value.bgStyle]}):null,o.value.imageDark?s("div",{class:"vp-blog-mask dark",style:[{background:`url(${o.value.imageDark}) center/cover no-repeat`},o.value.bgStyle]}):null],((u=t.heroInfo)==null?void 0:u.call(t,r.value))||[s(pe,{appear:!0,type:"group",delay:.04},()=>[r.value.image?s("img",{key:"light",class:["vp-blog-hero-image",{light:r.value.imageDark}],style:r.value.heroStyle,src:r.value.image,alt:r.value.alt}):null,r.value.imageDark?s("img",{key:"dark",class:"vp-blog-hero-image dark",style:r.value.heroStyle,src:r.value.imageDark,alt:r.value.alt}):null]),s(pe,{appear:!0,delay:.08},()=>r.value.text?s("h1",{class:"vp-blog-hero-title"},r.value.text):null),s(pe,{appear:!0,delay:.12},()=>r.value.tagline?s("p",{class:"vp-blog-hero-description",innerHTML:r.value.tagline}):null)],r.value.isFullScreen?s("button",{type:"button",class:"slide-down-button",onClick:()=>{window.scrollTo({top:a.value.clientHeight,behavior:"smooth"})}},[s(Za),s(Za)]):null])}}});const E2=["link","article","book","project","friend"];var x2=M({name:"ProjectPanel",components:{ArticleIcon:qn,BookIcon:Eu,FriendIcon:Tu,LinkIcon:xu,ProjectIcon:Lu},setup(){const e=Ee(),t=Il(),l=un(),n=(a="",i="icon")=>E2.includes(a)?s(Je(`${a}-icon`)):Zt(a)?s("img",{class:"vp-project-image",src:a,alt:i}):na(a)?s("img",{class:"vp-project-image",src:Le(a),alt:i}):s(Ne,{icon:a});return()=>{var a;return(a=e.value.projects)!=null&&a.length?s("div",{class:"vp-project-panel"},e.value.projects.map(({icon:i,link:r,name:o,desc:c},u)=>s("div",{class:["vp-project-card",{[`project${u%9}`]:!t.value}],onClick:()=>l(r)},[n(i,o),s("div",{class:"vp-project-name"},o),s("div",{class:"vp-project-desc"},c)]))):null}}}),L2=M({name:"BlogHome",setup(){const e=gn();return()=>s("div",{class:"vp-page vp-blog"},[s(w2),s("div",{class:"blog-page-wrapper"},[s("main",{id:"main-content",class:"vp-blog-main"},[s(pe,{appear:!0,delay:.16},()=>s(x2)),s(pe,{appear:!0,delay:.24},()=>s(Gi,{items:e.value.items}))]),s(pe,{appear:!0,delay:.16},()=>s(sa,{key:"blog"}))]),s(pe,{appear:!0,delay:.28},()=>s(ji))])}}),T2=M({name:"BlogHome",setup(){return()=>s(ra,()=>s(L2))}}),Cu=M({name:"ArticleType",setup(){const e=ue(),t=mt(),l=ie(),n=gn(),a=zi(),i=E(()=>{const r=l.value.blogLocales;return[{text:r.all,path:n.value.path},{text:r.star,path:a.value.path},...[].map(({key:o,path:c})=>({text:r[o],path:c.replace(/^\//,t.value)}))]});return()=>s("ul",{class:"vp-article-type-wrapper"},i.value.map(r=>s("li",{class:["vp-article-type",{active:r.path===e.value.path}]},s(Re,{to:r.path},()=>r.text))))}}),A2=M({name:"BlogPage",setup(){const e=ia(),t=Ee(),l=ue(),n=gn(),a=zi(),i=E(()=>{const{key:r="",type:o}=t.value.blog||{};return r==="star"?a.value.items:o==="type"&&r?e.value.items:n.value.items});return()=>s(ra,()=>s("div",{class:"vp-page vp-blog"},s("div",{class:"blog-page-wrapper"},[s("main",{id:"main-content",class:"vp-blog-main"},[s(pe,()=>s(Cu)),s(pe,{appear:!0,delay:.24},()=>s(Gi,{key:l.value.path,items:i.value}))]),s(pe,{delay:.16},()=>s(sa,{key:"blog"}))])))}}),I2=M({name:"TimelineItems",setup(){const e=vn(),t=ie(),l=Hi(),n=E(()=>e.value.timeline||t.value.blogLocales.timelineTitle),a=E(()=>l.value.config.map(({year:i})=>({title:i.toString(),level:2,slug:i.toString(),children:[]})));return()=>s("div",{class:"timeline-wrapper"},s("ul",{class:"timeline-content"},[s(pe,()=>s("li",{class:"motto"},n.value)),s(uu,{items:a.value}),l.value.config.map(({year:i,items:r},o)=>s(pe,{appear:!0,delay:.08*(o+1),type:"group"},()=>[s("h3",{key:"title",id:i,class:"timeline-year-title"},s("span",i)),s("li",{key:"content",class:"timeline-year-list"},[s("ul",{class:"timeline-year-wrapper"},r.map(({date:c,info:u,path:d})=>s("li",{class:"timeline-item"},[s("span",{class:"timeline-date"},c),s(Re,{class:"timeline-title",to:d},()=>u[be.title])])))])]))]))}}),P2=M({name:"Timeline",components:{ArticleType:Cu,CategoryList:Wi,TagList:Ki},setup(){return()=>s(ra,()=>s("div",{class:"vp-page vp-blog"},s("div",{class:"blog-page-wrapper"},[s("main",{id:"main-content",class:"vp-blog-main"},[s(pe,{appear:!0,delay:.24},()=>s(I2))]),s(pe,{delay:.16},()=>s(sa,{key:"blog"}))])))}});sv(Ne);const O2=dt({enhance:({app:e,router:t})=>{const{scrollBehavior:l}=t.options;t.options.scrollBehavior=async(...n)=>(await du().wait(),l(...n)),tf(e),e.component("HopeIcon",Ne),e.component("VPLink",Re),e.component("BloggerInfo",qi)},setup:()=>{lf(),sf(),g2()},layouts:{Layout:Zf,NotFound:t2,BlogCategory:k2,BlogHome:T2,BlogType:A2,Timeline:P2}}),C2=e=>e instanceof Element?document.activeElement===e&&(["TEXTAREA","SELECT","INPUT"].includes(e.tagName)||e.hasAttribute("contenteditable")):!1,R2=(e,t)=>t.some(l=>{if(ae(l))return l===e.key;const{key:n,ctrl:a=!1,shift:i=!1,alt:r=!1}=l;return n===e.key&&a===e.ctrlKey&&i===e.shiftKey&&r===e.altKey}),S2=/[^\x00-\x7F]/,D2=e=>e.split(/\s+/g).map(t=>t.trim()).filter(t=>!!t),xs=e=>e.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),Ls=(e,t)=>{const l=t.join(" "),n=D2(e);if(S2.test(e))return n.some(r=>l.toLowerCase().indexOf(r)>-1);const a=e.endsWith(" ");return new RegExp(n.map((r,o)=>n.length===o+1&&!a?`(?=.*\\b${xs(r)})`:`(?=.*\\b${xs(r)}\\b)`).join("")+".+","gi").test(l)},$2=({input:e,hotKeys:t})=>{if(t.value.length===0)return;const l=n=>{e.value&&R2(n,t.value)&&!C2(n.target)&&(n.preventDefault(),e.value.focus())};ke(()=>{document.addEventListener("keydown",l)}),Yn(()=>{document.removeEventListener("keydown",l)})},M2=[{title:"levy's blog",headers:[],path:"/",pathLocale:"/",extraFields:[]},{title:"关于",headers:[],path:"/about.html",pathLocale:"/",extraFields:[]},{title:"VuePress2 娱乐视频",headers:[],path:"/daily/a-vuepress2-entertaining-video.html",pathLocale:"/",extraFields:[]},{title:"来自Navicat的侵权警告",headers:[],path:"/daily/a-warning-from-navicat.html",pathLocale:"/",extraFields:[]},{title:"关于 Arm 你需要了解的三件事",headers:[],path:"/daily/about-arm-things-you-need-to-know.html",pathLocale:"/",extraFields:[]},{title:"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?",headers:[{level:2,title:"Background",slug:"background",link:"#background",children:[]},{level:2,title:"utf8mb4(UTF-8 MultiByte 4-Byte)",slug:"utf8mb4-utf-8-multibyte-4-byte",link:"#utf8mb4-utf-8-multibyte-4-byte",children:[]},{level:2,title:"utf8mb4_unicode_ci",slug:"utf8mb4-unicode-ci",link:"#utf8mb4-unicode-ci",children:[]},{level:2,title:"Some tips",slug:"some-tips",link:"#some-tips",children:[]}],path:"/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html",pathLocale:"/",extraFields:[]},{title:"Claude AI应用案例,从HTML中抽取文本",headers:[],path:"/daily/claude-ai-in-action-extract-info-from-html.html",pathLocale:"/",extraFields:[]},{title:"复制代码也许不是罪",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"正文",slug:"正文",link:"#正文",children:[]}],path:"/daily/copy-code-may-not-be-guilty.html",pathLocale:"/",extraFields:[]},{title:"不要与傻逼进行争吵",headers:[],path:"/daily/dont-try-to-argue-with-a-sb.html",pathLocale:"/",extraFields:[]},{title:"迭代复盘之三员管理",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"动手前,至少梳理出接口清单",slug:"动手前-至少梳理出接口清单",link:"#动手前-至少梳理出接口清单",children:[]},{level:2,title:"迁移时,采取结队编程",slug:"迁移时-采取结队编程",link:"#迁移时-采取结队编程",children:[]},{level:2,title:"迁移后,需要对自己负责的功能设计测试用例",slug:"迁移后-需要对自己负责的功能设计测试用例",link:"#迁移后-需要对自己负责的功能设计测试用例",children:[]}],path:"/daily/iteration-retrospective-of-sanyuan.html",pathLocale:"/",extraFields:[]},{title:"都什么年代了,还在用传统方式写代码?",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"开发流程",slug:"开发流程",link:"#开发流程",children:[]},{level:2,title:"程序设计",slug:"程序设计",link:"#程序设计",children:[]},{level:2,title:"代码编写",slug:"代码编写",link:"#代码编写",children:[{level:3,title:"生成真实代码",slug:"生成真实代码",link:"#生成真实代码",children:[]},{level:3,title:"辅助编程工具",slug:"辅助编程工具",link:"#辅助编程工具",children:[]}]},{level:2,title:"软件测试",slug:"软件测试",link:"#软件测试",children:[]},{level:2,title:"总结",slug:"总结",link:"#总结",children:[]},{level:2,title:"附:CodeWhisperer 安装",slug:"附-codewhisperer-安装",link:"#附-codewhisperer-安装",children:[]}],path:"/daily/leverage-ai-to-boost-coding-productivity.html",pathLocale:"/",extraFields:[]},{title:"微软中国CTO演讲观后感",headers:[],path:"/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html",pathLocale:"/",extraFields:[]},{title:"生产教训:测试环境要与生产环境一致",headers:[{level:2,title:"事件还原",slug:"事件还原",link:"#事件还原",children:[]},{level:2,title:"分析",slug:"分析",link:"#分析",children:[]},{level:2,title:"结论",slug:"结论",link:"#结论",children:[]}],path:"/daily/testing-environments-should-be-consistent-with-production-environments.html",pathLocale:"/",extraFields:[]},{title:"对Vue不得不吐槽的事",headers:[],path:"/daily/things-I-have-to-vent-about-vue.html",pathLocale:"/",extraFields:[]},{title:"再见ChatGPT,我选择Claude2!",headers:[],path:"/daily/use-claude2-instead-of-chatgpt.html",pathLocale:"/",extraFields:[]},{title:"Vim 作者离世",headers:[],path:"/daily/vim-creator-pass-away.html",pathLocale:"/",extraFields:[]},{title:"sh与bash的区别",headers:[],path:"/daily/what-is-the-difference-between-sh-and-bash.html",pathLocale:"/",extraFields:[]},{title:"技术点评:别每张表都加tenant_id",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"正文",slug:"正文",link:"#正文",children:[]}],path:"/daily/you-dont-need-to-add-tenant_id-to-every-table.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第一册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"值得一读的文章",slug:"值得一读的文章",link:"#值得一读的文章",children:[]}],path:"/english/contemporary-college-english-1.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第二册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"值得一读的文章",slug:"值得一读的文章",link:"#值得一读的文章",children:[]}],path:"/english/contemporary-college-english-2.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第三册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"值得一读的文章",slug:"值得一读的文章",link:"#值得一读的文章",children:[]}],path:"/english/contemporary-college-english-3.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第四册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"值得一读的文章",slug:"值得一读的文章",link:"#值得一读的文章",children:[]}],path:"/english/contemporary-college-english-4.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第五册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"Who Are You and What Are You Doing Here",slug:"who-are-you-and-what-are-you-doing-here",link:"#who-are-you-and-what-are-you-doing-here",children:[]},{level:2,title:"Two Kinds",slug:"two-kinds",link:"#two-kinds",children:[]},{level:2,title:"Love is a Fallacy",slug:"love-is-a-fallacy",link:"#love-is-a-fallacy",children:[]},{level:2,title:"Rewriting American History",slug:"rewriting-american-history",link:"#rewriting-american-history",children:[]},{level:2,title:"Nobel Peace Price About Global Warming",slug:"nobel-peace-price-about-global-warming",link:"#nobel-peace-price-about-global-warming",children:[]},{level:2,title:"The Bluest Eyes",slug:"the-bluest-eyes",link:"#the-bluest-eyes",children:[]},{level:2,title:"How News Becomes Options and Opinions Off-Limits",slug:"how-news-becomes-options-and-opinions-off-limits",link:"#how-news-becomes-options-and-opinions-off-limits",children:[]},{level:2,title:"The Indispensable Opposition",slug:"the-indispensable-opposition",link:"#the-indispensable-opposition",children:[]},{level:2,title:"The Danger of a Single Story",slug:"the-danger-of-a-single-story",link:"#the-danger-of-a-single-story",children:[]},{level:2,title:"Come Rain or Come Shine",slug:"come-rain-or-come-shine",link:"#come-rain-or-come-shine",children:[]},{level:2,title:"Invisible Man",slug:"invisible-man",link:"#invisible-man",children:[]},{level:2,title:"You've Got to Find What You Love",slug:"you-ve-got-to-find-what-you-love",link:"#you-ve-got-to-find-what-you-love",children:[]},{level:2,title:"Where Do We Go from Here",slug:"where-do-we-go-from-here",link:"#where-do-we-go-from-here",children:[]}],path:"/english/contemporary-college-english-5.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第六册",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"Paper Tigers",slug:"paper-tigers",link:"#paper-tigers",children:[]},{level:2,title:"What Is News",slug:"what-is-news",link:"#what-is-news",children:[]},{level:2,title:"At War with the Planet",slug:"at-war-with-the-planet",link:"#at-war-with-the-planet",children:[]},{level:2,title:"How to Get the Poor off Our Conscience",slug:"how-to-get-the-poor-off-our-conscience",link:"#how-to-get-the-poor-off-our-conscience",children:[]},{level:2,title:"Housewifely Arts",slug:"housewifely-arts",link:"#housewifely-arts",children:[]},{level:2,title:"The One Against The Many",slug:"the-one-against-the-many",link:"#the-one-against-the-many",children:[]},{level:2,title:"Notes on the English Character",slug:"notes-on-the-english-character",link:"#notes-on-the-english-character",children:[]},{level:2,title:"The Death of a Pig",slug:"the-death-of-a-pig",link:"#the-death-of-a-pig",children:[]},{level:2,title:"Don't Eat Fortune's Cookie",slug:"don-t-eat-fortune-s-cookie",link:"#don-t-eat-fortune-s-cookie",children:[]},{level:2,title:"The Accidental Universe",slug:"the-accidental-universe",link:"#the-accidental-universe",children:[]},{level:2,title:"Rowling's Speech at Harvard",slug:"rowling-s-speech-at-harvard",link:"#rowling-s-speech-at-harvard",children:[]}],path:"/english/contemporary-college-english-6.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语1:开篇",headers:[{level:2,title:"为什么学",slug:"为什么学",link:"#为什么学",children:[]},{level:2,title:"怎么学",slug:"怎么学",link:"#怎么学",children:[{level:3,title:"建设心态",slug:"建设心态",link:"#建设心态",children:[]},{level:3,title:"提升认知",slug:"提升认知",link:"#提升认知",children:[]},{level:3,title:"明确意义",slug:"明确意义",link:"#明确意义",children:[]},{level:3,title:"制定计划",slug:"制定计划",link:"#制定计划",children:[]},{level:3,title:"培养习惯",slug:"培养习惯",link:"#培养习惯",children:[]},{level:3,title:"注重方法",slug:"注重方法",link:"#注重方法",children:[]}]}],path:"/english/everyone-can-learn-english-1-overview.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语2:音标",headers:[{level:2,title:"方法",slug:"方法",link:"#方法",children:[]},{level:2,title:"讨论",slug:"讨论",link:"#讨论",children:[{level:3,title:"建议买实体书",slug:"建议买实体书",link:"#建议买实体书",children:[]},{level:3,title:"美语是主流",slug:"美语是主流",link:"#美语是主流",children:[]},{level:3,title:"不要纠结口音",slug:"不要纠结口音",link:"#不要纠结口音",children:[]}]}],path:"/english/everyone-can-learn-english-2-pronunciation.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语3:单词",headers:[{level:2,title:"方法",slug:"方法",link:"#方法",children:[]},{level:2,title:"提醒",slug:"提醒",link:"#提醒",children:[]}],path:"/english/everyone-can-learn-english-3-words.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语4:听说",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"方法",slug:"方法",link:"#方法",children:[{level:3,title:"1.建立信心",slug:"_1-建立信心",link:"#_1-建立信心",children:[]},{level:3,title:"2.明确方向",slug:"_2-明确方向",link:"#_2-明确方向",children:[]},{level:3,title:"3.坚持输入并输出",slug:"_3-坚持输入并输出",link:"#_3-坚持输入并输出",children:[]}]}],path:"/english/everyone-can-learn-english-4-listening-and-speaking.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语5:读写",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"阅读能力升级之旅",slug:"阅读能力升级之旅",link:"#阅读能力升级之旅",children:[]},{level:2,title:"阅读方法",slug:"阅读方法",link:"#阅读方法",children:[{level:3,title:"建立信心",slug:"建立信心",link:"#建立信心",children:[]},{level:3,title:"根据蓝思值选书",slug:"根据蓝思值选书",link:"#根据蓝思值选书",children:[]},{level:3,title:"巧查生词",slug:"巧查生词",link:"#巧查生词",children:[]},{level:3,title:"阅读材料推荐",slug:"阅读材料推荐",link:"#阅读材料推荐",children:[]}]},{level:2,title:"写作",slug:"写作",link:"#写作",children:[]}],path:"/english/everyone-can-learn-english-5-reading-and-writing.html",pathLocale:"/",extraFields:[]},{title:"英文能力评测手把手教学",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"单词量",slug:"单词量",link:"#单词量",children:[]},{level:2,title:"阅读能力",slug:"阅读能力",link:"#阅读能力",children:[]}],path:"/english/how-to-self-evaluate-english-level.html",pathLocale:"/",extraFields:[]},{title:"完成刷7k单词任务",headers:[],path:"/english/learning-7000-words-task-completed.html",pathLocale:"/",extraFields:[]},{title:"让 ChatGPT 成为你的外语私教",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"准备工作",slug:"准备工作",link:"#准备工作",children:[]},{level:2,title:"常用Prompt",slug:"常用prompt",link:"#常用prompt",children:[]},{level:2,title:"进行对话",slug:"进行对话",link:"#进行对话",children:[]},{level:2,title:"记录回答",slug:"记录回答",link:"#记录回答",children:[]}],path:"/english/let-chatgpt-be-your-foreign-language-teacher.html",pathLocale:"/",extraFields:[]},{title:"旧文章精选",headers:[],path:"/frontend/old-articles.html",pathLocale:"/",extraFields:[]},{title:"前端项目性能优化实战",headers:[{level:2,title:"检测",slug:"检测",link:"#检测",children:[]},{level:2,title:"图片优化",slug:"图片优化",link:"#图片优化",children:[]},{level:2,title:"提高TTFB时间",slug:"提高ttfb时间",link:"#提高ttfb时间",children:[]},{level:2,title:"移除未使用的 Javascript",slug:"移除未使用的-javascript",link:"#移除未使用的-javascript",children:[]},{level:2,title:"延迟静态资源的加载",slug:"延迟静态资源的加载",link:"#延迟静态资源的加载",children:[]},{level:2,title:"启用文本压缩",slug:"启用文本压缩",link:"#启用文本压缩",children:[]},{level:2,title:"优化缓存策略",slug:"优化缓存策略",link:"#优化缓存策略",children:[]}],path:"/frontend/performance-optimization-in-action.html",pathLocale:"/",extraFields:[]},{title:"Git最佳实践",headers:[{level:2,title:"精简提交",slug:"精简提交",link:"#精简提交",children:[]},{level:2,title:"频繁提交",slug:"频繁提交",link:"#频繁提交",children:[]},{level:2,title:"不要提交不完整的改动",slug:"不要提交不完整的改动",link:"#不要提交不完整的改动",children:[]},{level:2,title:"提交前测试那些改动",slug:"提交前测试那些改动",link:"#提交前测试那些改动",children:[]},{level:2,title:"版本控制不是备份系统",slug:"版本控制不是备份系统",link:"#版本控制不是备份系统",children:[]},{level:2,title:"Github实例",slug:"github实例",link:"#github实例",children:[{level:3,title:"一个功能对应一个分支",slug:"一个功能对应一个分支",link:"#一个功能对应一个分支",children:[]},{level:3,title:"提交“瘦”的PR",slug:"提交-瘦-的pr",link:"#提交-瘦-的pr",children:[]},{level:3,title:"使用正确的标题",slug:"使用正确的标题",link:"#使用正确的标题",children:[]},{level:3,title:"根据模板填写PR描述",slug:"根据模板填写pr描述",link:"#根据模板填写pr描述",children:[]},{level:3,title:"自动关闭issue",slug:"自动关闭issue",link:"#自动关闭issue",children:[]},{level:3,title:"1+2 review 规则",slug:"_1-2-review-规则",link:"#_1-2-review-规则",children:[]},{level:3,title:"礼貌提问",slug:"礼貌提问",link:"#礼貌提问",children:[]}]},{level:2,title:"学习资源",slug:"学习资源",link:"#学习资源",children:[]}],path:"/git/git-best-pratices.html",pathLocale:"/",extraFields:[]},{title:"Git代码合并指南",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"功能分支合并长驻分支冲突",slug:"功能分支合并长驻分支冲突",link:"#功能分支合并长驻分支冲突",children:[{level:3,title:"解决思路",slug:"解决思路",link:"#解决思路",children:[]},{level:3,title:"操作步骤",slug:"操作步骤",link:"#操作步骤",children:[]}]},{level:2,title:"功能分支被污染",slug:"功能分支被污染",link:"#功能分支被污染",children:[{level:3,title:"解决思路",slug:"解决思路-1",link:"#解决思路-1",children:[]},{level:3,title:"操作步骤",slug:"操作步骤-1",link:"#操作步骤-1",children:[]}]},{level:2,title:"挑选别的分支部分代码合并",slug:"挑选别的分支部分代码合并",link:"#挑选别的分支部分代码合并",children:[{level:3,title:"解决思路",slug:"解决思路-2",link:"#解决思路-2",children:[]},{level:3,title:"操作步骤",slug:"操作步骤-2",link:"#操作步骤-2",children:[]}]}],path:"/git/git-definitive-guide-to-merge-code.html",pathLocale:"/",extraFields:[]},{title:"Git查看历史记录小技巧",headers:[],path:"/git/git-history-two-tricks-in-idea.html",pathLocale:"/",extraFields:[]},{title:"Git常用命令",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"配置",slug:"配置",link:"#配置",children:[]},{level:2,title:"增强",slug:"增强",link:"#增强",children:[]},{level:2,title:"记住账号密码",slug:"记住账号密码",link:"#记住账号密码",children:[]},{level:2,title:"初始化",slug:"初始化",link:"#初始化",children:[]},{level:2,title:"本地提交",slug:"本地提交",link:"#本地提交",children:[{level:3,title:"取消未暂存的修改",slug:"取消未暂存的修改",link:"#取消未暂存的修改",children:[]},{level:3,title:"取消add",slug:"取消add",link:"#取消add",children:[]},{level:3,title:"取消提交",slug:"取消提交",link:"#取消提交",children:[]},{level:3,title:"修正提交",slug:"修正提交",link:"#修正提交",children:[]},{level:3,title:"stash修改",slug:"stash修改",link:"#stash修改",children:[]},{level:3,title:"恢复stash",slug:"恢复stash",link:"#恢复stash",children:[]}]},{level:2,title:"分支管理",slug:"分支管理",link:"#分支管理",children:[{level:3,title:"创建分支",slug:"创建分支",link:"#创建分支",children:[]},{level:3,title:"查看远程分支",slug:"查看远程分支",link:"#查看远程分支",children:[]},{level:3,title:"创建干净历史分支",slug:"创建干净历史分支",link:"#创建干净历史分支",children:[]},{level:3,title:"删除分支",slug:"删除分支",link:"#删除分支",children:[]}]},{level:2,title:"远程仓库",slug:"远程仓库",link:"#远程仓库",children:[{level:3,title:"远程仓库管理",slug:"远程仓库管理",link:"#远程仓库管理",children:[]},{level:3,title:"浅克隆",slug:"浅克隆",link:"#浅克隆",children:[]},{level:3,title:"克隆指定分支",slug:"克隆指定分支",link:"#克隆指定分支",children:[]},{level:3,title:"克隆失败因为文件名太长",slug:"克隆失败因为文件名太长",link:"#克隆失败因为文件名太长",children:[]},{level:3,title:"强行推送",slug:"强行推送",link:"#强行推送",children:[]},{level:3,title:"取消错误的推送",slug:"取消错误的推送",link:"#取消错误的推送",children:[]}]},{level:2,title:"标签管理",slug:"标签管理",link:"#标签管理",children:[{level:3,title:"新建本地标签",slug:"新建本地标签",link:"#新建本地标签",children:[]},{level:3,title:"删除本地标签",slug:"删除本地标签",link:"#删除本地标签",children:[]},{level:3,title:"查看本地所有标签",slug:"查看本地所有标签",link:"#查看本地所有标签",children:[]},{level:3,title:"推送本地标签",slug:"推送本地标签",link:"#推送本地标签",children:[]},{level:3,title:"获取远程标签",slug:"获取远程标签",link:"#获取远程标签",children:[]},{level:3,title:"删除远程标签",slug:"删除远程标签",link:"#删除远程标签",children:[]}]},{level:2,title:"其他",slug:"其他",link:"#其他",children:[{level:3,title:"cherry-pick",slug:"cherry-pick",link:"#cherry-pick",children:[]},{level:3,title:"merge unrelated histories",slug:"merge-unrelated-histories",link:"#merge-unrelated-histories",children:[]},{level:3,title:"git log 丢失最新提交",slug:"git-log-丢失最新提交",link:"#git-log-丢失最新提交",children:[]},{level:3,title:"查看分支创建时间",slug:"查看分支创建时间",link:"#查看分支创建时间",children:[]},{level:3,title:"根据文件搜索历史",slug:"根据文件搜索历史",link:"#根据文件搜索历史",children:[]},{level:3,title:"从所有提交中删除一个文件",slug:"从所有提交中删除一个文件",link:"#从所有提交中删除一个文件",children:[]}]}],path:"/git/git-useful-commands.html",pathLocale:"/",extraFields:[]},{title:"GitLab CI",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"安装与配置",slug:"安装与配置",link:"#安装与配置",children:[{level:3,title:"GitLab Runner 安装",slug:"gitlab-runner-安装",link:"#gitlab-runner-安装",children:[]},{level:3,title:"GitLab Runner 注册",slug:"gitlab-runner-注册",link:"#gitlab-runner-注册",children:[]},{level:3,title:"提交.gitlab-ci.yml",slug:"提交-gitlab-ci-yml",link:"#提交-gitlab-ci-yml",children:[]}]},{level:2,title:"合并代码前进行检查",slug:"合并代码前进行检查",link:"#合并代码前进行检查",children:[{level:3,title:"背景",slug:"背景",link:"#背景",children:[]},{level:3,title:"设置MR检查",slug:"设置mr检查",link:"#设置mr检查",children:[]},{level:3,title:".gitlab-ci.yml 示例",slug:"gitlab-ci-yml-示例",link:"#gitlab-ci-yml-示例",children:[]},{level:3,title:"效果",slug:"效果",link:"#效果",children:[]}]},{level:2,title:"集成单元测试",slug:"集成单元测试",link:"#集成单元测试",children:[]},{level:2,title:"线上发布 jar",slug:"线上发布-jar",link:"#线上发布-jar",children:[{level:3,title:"Maven配置",slug:"maven配置",link:"#maven配置",children:[]},{level:3,title:".gitlab-ci.yml 配置",slug:"gitlab-ci-yml-配置",link:"#gitlab-ci-yml-配置",children:[]},{level:3,title:"拉取最新的jar",slug:"拉取最新的jar",link:"#拉取最新的jar",children:[]}]},{level:2,title:"保存中间产物",slug:"保存中间产物",link:"#保存中间产物",children:[]},{level:2,title:"其他问题与解决方案",slug:"其他问题与解决方案",link:"#其他问题与解决方案",children:[{level:3,title:"Node.js",slug:"node-js",link:"#node-js",children:[]},{level:3,title:"创建不了容器",slug:"创建不了容器",link:"#创建不了容器",children:[]},{level:3,title:"本地成功,流水线失败",slug:"本地成功-流水线失败",link:"#本地成功-流水线失败",children:[]}]},{level:2,title:"参考文档",slug:"参考文档",link:"#参考文档",children:[]}],path:"/git/gitlab-ci.html",pathLocale:"/",extraFields:[]},{title:"再论Git Flow",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"动机",slug:"动机",link:"#动机",children:[]},{level:2,title:"分析",slug:"分析",link:"#分析",children:[{level:3,title:"剔除代码",slug:"剔除代码",link:"#剔除代码",children:[]},{level:3,title:"再次提交",slug:"再次提交",link:"#再次提交",children:[]},{level:3,title:"比较优劣",slug:"比较优劣",link:"#比较优劣",children:[]}]},{level:2,title:"实例",slug:"实例",link:"#实例",children:[{level:3,title:"分支模型",slug:"分支模型",link:"#分支模型",children:[]},{level:3,title:"功能提交",slug:"功能提交",link:"#功能提交",children:[]},{level:3,title:"功能回撤",slug:"功能回撤",link:"#功能回撤",children:[]}]},{level:2,title:"结论",slug:"结论",link:"#结论",children:[]}],path:"/git/rethinking-git-flow.html",pathLocale:"/",extraFields:[]},{title:"操作 Gitlab MR 的命令行工具",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[{level:3,title:"解压zip",slug:"解压zip",link:"#解压zip",children:[]},{level:3,title:"安装git bash",slug:"安装git-bash",link:"#安装git-bash",children:[]}]},{level:2,title:"配置",slug:"配置",link:"#配置",children:[{level:3,title:"gitlab_token",slug:"gitlab-token",link:"#gitlab-token",children:[]},{level:3,title:"codebases",slug:"codebases",link:"#codebases",children:[]},{level:3,title:"环境变量",slug:"环境变量",link:"#环境变量",children:[]},{level:3,title:"IDEA",slug:"idea",link:"#idea",children:[]}]},{level:2,title:"使用",slug:"使用",link:"#使用",children:[{level:3,title:"创建MR",slug:"创建mr",link:"#创建mr",children:[]},{level:3,title:"查看MR",slug:"查看mr",link:"#查看mr",children:[]},{level:3,title:"合并MR",slug:"合并mr",link:"#合并mr",children:[]},{level:3,title:"冲突处理",slug:"冲突处理",link:"#冲突处理",children:[]}]}],path:"/git/use-command-line-tool-to-manage-gitlab-merge-request.html",pathLocale:"/",extraFields:[]},{title:"IDEA常见问题与解决方案",headers:[{level:2,title:"启动参数过长",slug:"启动参数过长",link:"#启动参数过长",children:[]},{level:2,title:"设置JDK版本",slug:"设置jdk版本",link:"#设置jdk版本",children:[]},{level:2,title:"lombok 编译报错",slug:"lombok-编译报错",link:"#lombok-编译报错",children:[]},{level:2,title:"设置启动参数",slug:"设置启动参数",link:"#设置启动参数",children:[]},{level:2,title:"栈溢出",slug:"栈溢出",link:"#栈溢出",children:[]},{level:2,title:"内存不足",slug:"内存不足",link:"#内存不足",children:[]},{level:2,title:"热加载",slug:"热加载",link:"#热加载",children:[]},{level:2,title:"终端加载环境变量",slug:"终端加载环境变量",link:"#终端加载环境变量",children:[]},{level:2,title:"添加外部jar作为依赖",slug:"添加外部jar作为依赖",link:"#添加外部jar作为依赖",children:[]},{level:2,title:"文件找不到——依赖冲突",slug:"文件找不到——依赖冲突",link:"#文件找不到——依赖冲突",children:[]},{level:2,title:"自动import",slug:"自动import",link:"#自动import",children:[]},{level:2,title:"文件乱码",slug:"文件乱码",link:"#文件乱码",children:[]},{level:2,title:"autowired 提示变量未赋值",slug:"autowired-提示变量未赋值",link:"#autowired-提示变量未赋值",children:[]}],path:"/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html",pathLocale:"/",extraFields:[]},{title:"Maven常见问题与解决方案",headers:[{level:2,title:"运行 class 找不到主类",slug:"运行-class-找不到主类",link:"#运行-class-找不到主类",children:[]},{level:2,title:"运行 jar 找不到主类",slug:"运行-jar-找不到主类",link:"#运行-jar-找不到主类",children:[]},{level:2,title:"编译时找不到主类",slug:"编译时找不到主类",link:"#编译时找不到主类",children:[]},{level:2,title:"设置Maven目录",slug:"设置maven目录",link:"#设置maven目录",children:[]},{level:2,title:"无法识别 Maven 项目",slug:"无法识别-maven-项目",link:"#无法识别-maven-项目",children:[]},{level:2,title:"使用了不想要的镜像源",slug:"使用了不想要的镜像源",link:"#使用了不想要的镜像源",children:[]},{level:2,title:"下载 jar 失败",slug:"下载-jar-失败",link:"#下载-jar-失败",children:[]},{level:2,title:"私服认证401",slug:"私服认证401",link:"#私服认证401",children:[]},{level:2,title:"避免缓存",slug:"避免缓存",link:"#避免缓存",children:[]},{level:2,title:"参考资料",slug:"参考资料",link:"#参考资料",children:[]}],path:"/java/Resolving-Common-Problems-in-Maven.md.html",pathLocale:"/",extraFields:[]},{title:"避免密码明文传输",headers:[{level:2,title:"说明",slug:"说明",link:"#说明",children:[]},{level:2,title:"前端代码",slug:"前端代码",link:"#前端代码",children:[]},{level:2,title:"后端代码",slug:"后端代码",link:"#后端代码",children:[]}],path:"/java/avoid-sending-password-in-plaintext.html",pathLocale:"/",extraFields:[]},{title:"检查名字是否重复",headers:[{level:2,title:"推荐做法",slug:"推荐做法",link:"#推荐做法",children:[]},{level:2,title:"Why",slug:"why",link:"#why",children:[]}],path:"/java/check-if-name-exists.html",pathLocale:"/",extraFields:[]},{title:"Excel处理常用实践",headers:[{level:2,title:"基础知识",slug:"基础知识",link:"#基础知识",children:[]},{level:2,title:"应用框架",slug:"应用框架",link:"#应用框架",children:[{level:3,title:"导出",slug:"导出",link:"#导出",children:[]},{level:3,title:"导入",slug:"导入",link:"#导入",children:[]}]},{level:2,title:"常见问题与解决方案",slug:"常见问题与解决方案",link:"#常见问题与解决方案",children:[{level:3,title:"浏览器下载",slug:"浏览器下载",link:"#浏览器下载",children:[]},{level:3,title:"上传文件大小限制",slug:"上传文件大小限制",link:"#上传文件大小限制",children:[]},{level:3,title:"缺少字体",slug:"缺少字体",link:"#缺少字体",children:[]},{level:3,title:"序列化失败",slug:"序列化失败",link:"#序列化失败",children:[]}]}],path:"/java/common-practices-for-handling-excel.html",pathLocale:"/",extraFields:[]},{title:"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"下载",slug:"下载",link:"#下载",children:[]},{level:2,title:"修改",slug:"修改",link:"#修改",children:[]},{level:2,title:"上传",slug:"上传",link:"#上传",children:[]}],path:"/java/how-to-convert-snapshot-into-release-jar-without-source-code.html",pathLocale:"/",extraFields:[]},{title:"集合命名推荐",headers:[{level:2,title:"概述",slug:"概述",link:"#概述",children:[]},{level:2,title:"List",slug:"list",link:"#list",children:[]},{level:2,title:"Set",slug:"set",link:"#set",children:[]},{level:2,title:"Map",slug:"map",link:"#map",children:[]}],path:"/java/recommend-practices-for-collections-naming-convention.html",pathLocale:"/",extraFields:[]},{title:"根据时间范围查询推荐实践",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"需求",slug:"需求",link:"#需求",children:[]},{level:2,title:"分析",slug:"分析",link:"#分析",children:[]},{level:2,title:"实现",slug:"实现",link:"#实现",children:[{level:3,title:"MySQL",slug:"mysql",link:"#mysql",children:[]},{level:3,title:"MyBatis",slug:"mybatis",link:"#mybatis",children:[]},{level:3,title:"LoxalDate",slug:"loxaldate",link:"#loxaldate",children:[]},{level:3,title:"Jackson",slug:"jackson",link:"#jackson",children:[]}]},{level:2,title:"结语",slug:"结语",link:"#结语",children:[]}],path:"/java/recommend-practices-for-query-by-date-range.html",pathLocale:"/",extraFields:[]},{title:"编写函数的最佳实践",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[{level:3,title:"减少重复",slug:"减少重复",link:"#减少重复",children:[]},{level:3,title:"隐藏细节",slug:"隐藏细节",link:"#隐藏细节",children:[]}]},{level:2,title:"建议",slug:"建议",link:"#建议",children:[{level:3,title:"优先根据业务命名",slug:"优先根据业务命名",link:"#优先根据业务命名",children:[]},{level:3,title:"一个函数只做一件事",slug:"一个函数只做一件事",link:"#一个函数只做一件事",children:[]},{level:3,title:"优先使用纯函数",slug:"优先使用纯函数",link:"#优先使用纯函数",children:[]},{level:3,title:"编写不需要返回值的函数",slug:"编写不需要返回值的函数",link:"#编写不需要返回值的函数",children:[]}]},{level:2,title:"参考资料",slug:"参考资料",link:"#参考资料",children:[]}],path:"/java/recommend-practices-for-writing-good-functions.html",pathLocale:"/",extraFields:[]},{title:"枚举的推荐实践",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"Java",slug:"java",link:"#java",children:[{level:3,title:"极简实现",slug:"极简实现",link:"#极简实现",children:[]},{level:3,title:"常见实现",slug:"常见实现",link:"#常见实现",children:[]},{level:3,title:"更好的方式",slug:"更好的方式",link:"#更好的方式",children:[]},{level:3,title:"为什么还要定义数字?",slug:"为什么还要定义数字",link:"#为什么还要定义数字",children:[]}]},{level:2,title:"数据库",slug:"数据库",link:"#数据库",children:[{level:3,title:"排序特点",slug:"排序特点",link:"#排序特点",children:[]},{level:3,title:"添加新值",slug:"添加新值",link:"#添加新值",children:[]}]},{level:2,title:"总结",slug:"总结",link:"#总结",children:[]},{level:2,title:"参考资料",slug:"参考资料",link:"#参考资料",children:[]}],path:"/java/using-enum-in-java.html",pathLocale:"/",extraFields:[]},{title:"Boolean 还是 boolean?",headers:[{level:2,title:"结论",slug:"结论",link:"#结论",children:[]},{level:2,title:"争议",slug:"争议",link:"#争议",children:[]},{level:2,title:"实战",slug:"实战",link:"#实战",children:[{level:3,title:"简单例子",slug:"简单例子",link:"#简单例子",children:[]},{level:3,title:"复杂例子",slug:"复杂例子",link:"#复杂例子",children:[]}]},{level:2,title:"附",slug:"附",link:"#附",children:[]}],path:"/java/which-one-is-better-Boolean-or-boolean.html",pathLocale:"/",extraFields:[]},{title:"forEach 还是 map?",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"结论",slug:"结论",link:"#结论",children:[]},{level:2,title:"解析",slug:"解析",link:"#解析",children:[]},{level:2,title:"实战",slug:"实战",link:"#实战",children:[]}],path:"/java/which-one-is-better-forEach-or-map.html",pathLocale:"/",extraFields:[]},{title:"Jackson 经典异常 UnrecognizedPropertyException",headers:[],path:"/java/why-i-prefer-fastjson-instead-of-jackson.html",pathLocale:"/",extraFields:[]},{title:"升个jar版本,怎么这么难?",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"接口调用示意图",slug:"接口调用示意图",link:"#接口调用示意图",children:[]},{level:2,title:"已知信息",slug:"已知信息",link:"#已知信息",children:[]},{level:2,title:"问题",slug:"问题",link:"#问题",children:[]},{level:2,title:"复盘",slug:"复盘",link:"#复盘",children:[]}],path:"/java/why-is-it-so-hard-to-upgrade-dependencies.html",pathLocale:"/",extraFields:[]},{title:"数据备份案例:mysqldump实战",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"架构",slug:"架构",link:"#架构",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[]},{level:2,title:"导出",slug:"导出",link:"#导出",children:[{level:3,title:"--set-gtid-purged=OFF",slug:"set-gtid-purged-off",link:"#set-gtid-purged-off",children:[]},{level:3,title:"--ignore-table",slug:"ignore-table",link:"#ignore-table",children:[]}]},{level:2,title:"导入",slug:"导入",link:"#导入",children:[]},{level:2,title:"为什么不?",slug:"为什么不",link:"#为什么不",children:[]}],path:"/mysql/mysql-backup-case-study-mysqldump-in-action.html",pathLocale:"/",extraFields:[]},{title:"数据迁移案例:表AUTO_INCREMENT加10w",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"备份",slug:"备份",link:"#备份",children:[]},{level:2,title:"SQL编写",slug:"sql编写",link:"#sql编写",children:[]},{level:2,title:"存储过程(可复用",slug:"存储过程-可复用",link:"#存储过程-可复用",children:[]}],path:"/mysql/mysql-data-migration-case-study-add-auto-increment.html",pathLocale:"/",extraFields:[]},{title:"MySQL 命令行执行SQL的细节",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"环境说明",slug:"环境说明",link:"#环境说明",children:[]},{level:2,title:"执行SQL文件",slug:"执行sql文件",link:"#执行sql文件",children:[]},{level:2,title:"复制粘贴执行",slug:"复制粘贴执行",link:"#复制粘贴执行",children:[]},{level:2,title:"如果要删除错误的数据怎么办?",slug:"如果要删除错误的数据怎么办",link:"#如果要删除错误的数据怎么办",children:[]}],path:"/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html",pathLocale:"/",extraFields:[]},{title:"Python 导出 MySQL 库表信息到 Excel",headers:[{level:2,title:"需求",slug:"需求",link:"#需求",children:[]},{level:2,title:"代码",slug:"代码",link:"#代码",children:[]},{level:2,title:"其他细节",slug:"其他细节",link:"#其他细节",children:[]}],path:"/python/export-mysql-table-into-excel.html",pathLocale:"/",extraFields:[]},{title:"单元测试概述",headers:[{level:2,title:"Why",slug:"why",link:"#why",children:[]},{level:2,title:"What",slug:"what",link:"#what",children:[]},{level:2,title:"How",slug:"how",link:"#how",children:[{level:3,title:"测试代码的风格",slug:"测试代码的风格",link:"#测试代码的风格",children:[]},{level:3,title:"测试难点",slug:"测试难点",link:"#测试难点",children:[]},{level:3,title:"常用工具",slug:"常用工具",link:"#常用工具",children:[]}]},{level:2,title:"Bad Examples",slug:"bad-examples",link:"#bad-examples",children:[{level:3,title:"没有测试类",slug:"没有测试类",link:"#没有测试类",children:[]},{level:3,title:"没有断言",slug:"没有断言",link:"#没有断言",children:[]},{level:3,title:"无法重复执行",slug:"无法重复执行",link:"#无法重复执行",children:[]}]}],path:"/software-testing/unit-testing-overview.html",pathLocale:"/",extraFields:[]},{title:"使用 RestAssured 进行 API 测试",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"What",slug:"what",link:"#what",children:[]},{level:2,title:"Why",slug:"why",link:"#why",children:[]},{level:2,title:"为什么不用Postman",slug:"为什么不用postman",link:"#为什么不用postman",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[]},{level:2,title:"快速上手",slug:"快速上手",link:"#快速上手",children:[]},{level:2,title:"通用设置",slug:"通用设置",link:"#通用设置",children:[]},{level:2,title:"请求示例",slug:"请求示例",link:"#请求示例",children:[]},{level:2,title:"接口依赖",slug:"接口依赖",link:"#接口依赖",children:[]},{level:2,title:"上传示例",slug:"上传示例",link:"#上传示例",children:[]},{level:2,title:"下载示例",slug:"下载示例",link:"#下载示例",children:[]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[]},{level:2,title:"其他问题",slug:"其他问题",link:"#其他问题",children:[{level:3,title:"为什么不用 Pytest",slug:"为什么不用-pytest",link:"#为什么不用-pytest",children:[]},{level:3,title:"这也是单元测试吗",slug:"这也是单元测试吗",link:"#这也是单元测试吗",children:[]}]},{level:2,title:"参考资料",slug:"参考资料",link:"#参考资料",children:[]}],path:"/software-testing/use-RestAssured-for-api-testing.html",pathLocale:"/",extraFields:[]},{title:"使用 Cypress 进行端对端测试",headers:[{level:2,title:"为什么写端对端测试",slug:"为什么写端对端测试",link:"#为什么写端对端测试",children:[]},{level:2,title:"为什么用 Cypress",slug:"为什么用-cypress",link:"#为什么用-cypress",children:[]},{level:2,title:"快速开始",slug:"快速开始",link:"#快速开始",children:[{level:3,title:"安装",slug:"安装",link:"#安装",children:[]},{level:3,title:"加速下载",slug:"加速下载",link:"#加速下载",children:[]},{level:3,title:"目录结构",slug:"目录结构",link:"#目录结构",children:[]},{level:3,title:"与 Jest 协同工作",slug:"与-jest-协同工作",link:"#与-jest-协同工作",children:[]},{level:3,title:"检查依赖及生产安装依赖命令",slug:"检查依赖及生产安装依赖命令",link:"#检查依赖及生产安装依赖命令",children:[]},{level:3,title:"第一个用例",slug:"第一个用例",link:"#第一个用例",children:[]},{level:3,title:"更复杂的示例",slug:"更复杂的示例",link:"#更复杂的示例",children:[]}]},{level:2,title:"结合TypeScript",slug:"结合typescript",link:"#结合typescript",children:[]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[{level:3,title:"直接运行 Cypress",slug:"直接运行-cypress",link:"#直接运行-cypress",children:[]},{level:3,title:"使用 start-server-and-test",slug:"使用-start-server-and-test",link:"#使用-start-server-and-test",children:[]},{level:3,title:"This job is stuck",slug:"this-job-is-stuck",link:"#this-job-is-stuck",children:[]},{level:3,title:"Cypress Dashbord",slug:"cypress-dashbord",link:"#cypress-dashbord",children:[]}]},{level:2,title:"总结",slug:"总结",link:"#总结",children:[]},{level:2,title:"拓展阅读",slug:"拓展阅读",link:"#拓展阅读",children:[]}],path:"/software-testing/use-cypress-for-e2e-testing.html",pathLocale:"/",extraFields:[]},{title:"使用 Jest 实践测试驱动开发",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"环境搭建",slug:"环境搭建",link:"#环境搭建",children:[]},{level:2,title:"开发",slug:"开发",link:"#开发",children:[{level:3,title:"文件初始化",slug:"文件初始化",link:"#文件初始化",children:[]},{level:3,title:"第一个用例",slug:"第一个用例",link:"#第一个用例",children:[]},{level:3,title:"第二个用例",slug:"第二个用例",link:"#第二个用例",children:[]},{level:3,title:"第三个用例",slug:"第三个用例",link:"#第三个用例",children:[]},{level:3,title:"第四个用例",slug:"第四个用例",link:"#第四个用例",children:[]},{level:3,title:"第五个用例",slug:"第五个用例",link:"#第五个用例",children:[]},{level:3,title:"重构",slug:"重构",link:"#重构",children:[]}]},{level:2,title:"结论",slug:"结论",link:"#结论",children:[]}],path:"/software-testing/use-jest-for-test-driven-development.html",pathLocale:"/",extraFields:[]},{title:"下一代 UI 自动化测试工具 Playwright",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[]},{level:2,title:"使用",slug:"使用",link:"#使用",children:[{level:3,title:"代码生成",slug:"代码生成",link:"#代码生成",children:[]},{level:3,title:"修改代码",slug:"修改代码",link:"#修改代码",children:[]},{level:3,title:"执行用例",slug:"执行用例",link:"#执行用例",children:[]},{level:3,title:"调试用例",slug:"调试用例",link:"#调试用例",children:[]},{level:3,title:"查看报告",slug:"查看报告",link:"#查看报告",children:[]}]},{level:2,title:"常见场景与解决方案",slug:"常见场景与解决方案",link:"#常见场景与解决方案",children:[{level:3,title:"应用登录",slug:"应用登录",link:"#应用登录",children:[]},{level:3,title:"环境变量",slug:"环境变量",link:"#环境变量",children:[]},{level:3,title:"超时时间",slug:"超时时间",link:"#超时时间",children:[]},{level:3,title:"元素选择",slug:"元素选择",link:"#元素选择",children:[]},{level:3,title:"声明断言 && 检查元素是否存在",slug:"声明断言-检查元素是否存在",link:"#声明断言-检查元素是否存在",children:[]},{level:3,title:"获取第n个元素",slug:"获取第n个元素",link:"#获取第n个元素",children:[]},{level:3,title:"遍历元素",slug:"遍历元素",link:"#遍历元素",children:[]},{level:3,title:"获取元素属性",slug:"获取元素属性",link:"#获取元素属性",children:[]},{level:3,title:"判断子元素数量",slug:"判断子元素数量",link:"#判断子元素数量",children:[]},{level:3,title:"鼠标悬浮",slug:"鼠标悬浮",link:"#鼠标悬浮",children:[]},{level:3,title:"操作剪贴板",slug:"操作剪贴板",link:"#操作剪贴板",children:[]}]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[]},{level:2,title:"其他",slug:"其他",link:"#其他",children:[{level:3,title:"setup.py bdist_wheel did not run successfully",slug:"setup-py-bdist-wheel-did-not-run-successfully",link:"#setup-py-bdist-wheel-did-not-run-successfully",children:[]}]}],path:"/software-testing/use-playwright-for-ui-testing.html",pathLocale:"/",extraFields:[]},{title:"使用 Postman 进行 API 测试",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"本地调试",slug:"本地调试",link:"#本地调试",children:[{level:3,title:"接口集合",slug:"接口集合",link:"#接口集合",children:[]},{level:3,title:"从 cURL 导入",slug:"从-curl-导入",link:"#从-curl-导入",children:[]},{level:3,title:"环境变量",slug:"环境变量",link:"#环境变量",children:[]},{level:3,title:"请求设置",slug:"请求设置",link:"#请求设置",children:[]}]},{level:2,title:"接口测试",slug:"接口测试",link:"#接口测试",children:[{level:3,title:"编写用例",slug:"编写用例",link:"#编写用例",children:[]},{level:3,title:"上传文件",slug:"上传文件",link:"#上传文件",children:[]},{level:3,title:"运行集合",slug:"运行集合",link:"#运行集合",children:[]}]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[{level:3,title:"导出接口",slug:"导出接口",link:"#导出接口",children:[]},{level:3,title:"导出环境变量",slug:"导出环境变量",link:"#导出环境变量",children:[]},{level:3,title:"复制要上传的文件",slug:"复制要上传的文件",link:"#复制要上传的文件",children:[]},{level:3,title:"提交到Git",slug:"提交到git",link:"#提交到git",children:[]},{level:3,title:"建立CI任务",slug:"建立ci任务",link:"#建立ci任务",children:[]}]}],path:"/software-testing/use-postman-for-api-testing.html",pathLocale:"/",extraFields:[]},{title:"科学上网",headers:[{level:2,title:"说明",slug:"说明",link:"#说明",children:[]},{level:2,title:"购买指南",slug:"购买指南",link:"#购买指南",children:[]},{level:2,title:"客户端",slug:"客户端",link:"#客户端",children:[]},{level:2,title:"导入配置",slug:"导入配置",link:"#导入配置",children:[]},{level:2,title:"其他",slug:"其他",link:"#其他",children:[{level:3,title:"500 内部代理错误",slug:"_500-内部代理错误",link:"#_500-内部代理错误",children:[]},{level:3,title:"修改PAC文件",slug:"修改pac文件",link:"#修改pac文件",children:[]},{level:3,title:"使用 New Bing",slug:"使用-new-bing",link:"#使用-new-bing",children:[]},{level:3,title:"申请美区apple id",slug:"申请美区apple-id",link:"#申请美区apple-id",children:[]}]}],path:"/tools/how-to-connect-to-internet.html",pathLocale:"/",extraFields:[]},{title:"",headers:[],path:"/404.html",pathLocale:"/",extraFields:[]},{title:"Daily",headers:[],path:"/daily/",pathLocale:"/",extraFields:[]},{title:"English",headers:[],path:"/english/",pathLocale:"/",extraFields:[]},{title:"Frontend",headers:[],path:"/frontend/",pathLocale:"/",extraFields:[]},{title:"Git",headers:[],path:"/git/",pathLocale:"/",extraFields:[]},{title:"Java",headers:[],path:"/java/",pathLocale:"/",extraFields:[]},{title:"Mysql",headers:[],path:"/mysql/",pathLocale:"/",extraFields:[]},{title:"Python",headers:[],path:"/python/",pathLocale:"/",extraFields:[]},{title:"Software Testing",headers:[],path:"/software-testing/",pathLocale:"/",extraFields:[]},{title:"Tools",headers:[],path:"/tools/",pathLocale:"/",extraFields:[]},{title:"分类",headers:[],path:"/category/",pathLocale:"/",extraFields:[]},{title:"标签",headers:[],path:"/tag/",pathLocale:"/",extraFields:[]},{title:"文章",headers:[],path:"/article/",pathLocale:"/",extraFields:[]},{title:"收藏",headers:[],path:"/star/",pathLocale:"/",extraFields:[]},{title:"时间轴",headers:[],path:"/timeline/",pathLocale:"/",extraFields:[]},{title:"标签: Daily",headers:[],path:"/tag/daily/",pathLocale:"/",extraFields:[]},{title:"标签: Frontend",headers:[],path:"/tag/frontend/",pathLocale:"/",extraFields:[]},{title:"标签: Video",headers:[],path:"/tag/video/",pathLocale:"/",extraFields:[]},{title:"标签: Linux",headers:[],path:"/tag/linux/",pathLocale:"/",extraFields:[]},{title:"标签: Docker",headers:[],path:"/tag/docker/",pathLocale:"/",extraFields:[]},{title:"标签: MySQL",headers:[],path:"/tag/mysql/",pathLocale:"/",extraFields:[]},{title:"标签: AI",headers:[],path:"/tag/ai/",pathLocale:"/",extraFields:[]},{title:"标签: Emotion",headers:[],path:"/tag/emotion/",pathLocale:"/",extraFields:[]},{title:"标签: Working Experience",headers:[],path:"/tag/working-experience/",pathLocale:"/",extraFields:[]},{title:"标签: Tool",headers:[],path:"/tag/tool/",pathLocale:"/",extraFields:[]},{title:"标签: Design",headers:[],path:"/tag/design/",pathLocale:"/",extraFields:[]},{title:"标签: English",headers:[],path:"/tag/english/",pathLocale:"/",extraFields:[]},{title:"标签: Git",headers:[],path:"/tag/git/",pathLocale:"/",extraFields:[]},{title:"标签: GitLab",headers:[],path:"/tag/gitlab/",pathLocale:"/",extraFields:[]},{title:"标签: Java",headers:[],path:"/tag/java/",pathLocale:"/",extraFields:[]},{title:"标签: Node.js",headers:[],path:"/tag/node.js/",pathLocale:"/",extraFields:[]},{title:"标签: Python",headers:[],path:"/tag/python/",pathLocale:"/",extraFields:[]},{title:"标签: JavaScript",headers:[],path:"/tag/javascript/",pathLocale:"/",extraFields:[]},{title:"标签: Testing",headers:[],path:"/tag/testing/",pathLocale:"/",extraFields:[]}],V2=W(M2),F2=()=>V2,N2=({searchIndex:e,routeLocale:t,query:l,maxSuggestions:n})=>{const a=E(()=>e.value.filter(i=>i.pathLocale===t.value));return E(()=>{const i=l.value.trim().toLowerCase();if(!i)return[];const r=[],o=(c,u)=>{Ls(i,[u.title])&&r.push({link:`${c.path}#${u.slug}`,title:c.title,header:u.title});for(const d of u.children){if(r.length>=n.value)return;o(c,d)}};for(const c of a.value){if(r.length>=n.value)break;if(Ls(i,[c.title,...c.extraFields])){r.push({link:c.path,title:c.title});continue}for(const u of c.headers){if(r.length>=n.value)break;o(c,u)}}return r})},j2=e=>{const t=W(0);return{focusIndex:t,focusNext:()=>{t.value{t.value>0?t.value-=1:t.value=e.value.length-1}}},B2=M({name:"SearchBox",props:{locales:{type:Object,required:!1,default:()=>({})},hotKeys:{type:Array,required:!1,default:()=>[]},maxSuggestions:{type:Number,required:!1,default:5}},setup(e){const{locales:t,hotKeys:l,maxSuggestions:n}=Sd(e),a=Ve(),i=mt(),r=F2(),o=W(null),c=W(!1),u=W(""),d=E(()=>t.value[i.value]??{}),p=N2({searchIndex:r,routeLocale:i,query:u,maxSuggestions:n}),{focusIndex:h,focusNext:v,focusPrev:b}=j2(p);$2({input:o,hotKeys:l});const w=E(()=>c.value&&!!p.value.length),x=()=>{w.value&&b()},g=()=>{w.value&&v()},_=P=>{if(!w.value)return;const C=p.value[P];C&&a.push(C.link).then(()=>{u.value="",h.value=0})};return()=>s("form",{class:"search-box",role:"search"},[s("input",{ref:o,type:"search",placeholder:d.value.placeholder,autocomplete:"off",spellcheck:!1,value:u.value,onFocus:()=>c.value=!0,onBlur:()=>c.value=!1,onInput:P=>u.value=P.target.value,onKeydown:P=>{switch(P.key){case"ArrowUp":{x();break}case"ArrowDown":{g();break}case"Enter":{P.preventDefault(),_(h.value);break}}}}),w.value&&s("ul",{class:"suggestions",onMouseleave:()=>h.value=-1},p.value.map(({link:P,title:C,header:B},R)=>s("li",{class:["suggestion",{focus:h.value===R}],onMouseenter:()=>h.value=R,onMousedown:()=>_(R)},s("a",{href:P,onClick:V=>V.preventDefault()},[s("span",{class:"page-title"},C),B&&s("span",{class:"page-header"},`> ${B}`)]))))])}});const z2={},H2=["s","/"],q2=5,U2=dt({enhance({app:e}){e.component("SearchBox",t=>s(B2,{locales:z2,hotKeys:H2,maxSuggestions:q2,...t}))}}),Cn=[N0,Z1,rv,dv,vv,yv,Ev,Ov,Dv,qv,O2,U2],G2=[["v-8daa1a0e","/",{y:"h",t:"levy's blog",i:"home",O:null},["/README.md"]],["v-22a39d25","/about.html",{y:"p",t:"关于",i:"circle-info",O:null},[":md"]],["v-30a50b00","/daily/a-vuepress2-entertaining-video.html",{d:1690848e6,l:"2023年8月1日",g:["Daily","Frontend","Video"],e:`

VuePress2 娱乐视频

+

参考《原神,启动》的梗,做的一个娱乐向视频。

+`,r:{minutes:.11,words:34},y:"a",t:"VuePress2 娱乐视频",O:-1690848e6},[":md"]],["v-0481cc80","/daily/a-warning-from-navicat.html",{d:16907616e5,l:"2023年7月31日",g:["Daily","Video"],e:`

来自Navicat的侵权警告

+

公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。

+

另外,开发者常用的软件的合法替代品,视频中也有推荐。

+`,r:{minutes:.27,words:81},y:"a",t:"来自Navicat的侵权警告",O:-16907616e5},[":md"]],["v-31eac486","/daily/about-arm-things-you-need-to-know.html",{d:16909344e5,l:"2023年8月2日",g:["Daily","Linux","Docker"],e:`

关于 Arm 你需要了解的三件事

+

Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。

+

跟我们有什么关系呢?

+
    +
  1. MacOS 的 M1 芯片是基于 Arm 的
  2. +
  3. 云厂商及生态都在积极与 Arm 进行合作
  4. +
  5. Docker 镜像的构建有注意事项
  6. +
+`,r:{minutes:.31,words:92},y:"a",t:"关于 Arm 你需要了解的三件事",O:-16909344e5},[":md"]],["v-79e139f8","/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html",{d:16920576e5,l:"2023年8月15日",g:["Daily","Video","MySQL"],e:`

Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

+

Background

+

Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci?

+
CREATE TABLE \`my_table\` (
+  \`id\` bigint NOT NULL AUTO_INCREMENT,
+  PRIMARY KEY (\`id\`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+

That is the knowledge that today I want to share with you.

+`,r:{minutes:1.78,words:533},y:"a",t:"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?",O:-16920576e5},[":md"]],["v-7a74360a","/daily/claude-ai-in-action-extract-info-from-html.html",{d:16929216e5,l:"2023年8月25日",g:["Daily","Video","AI"],e:`

Claude AI应用案例,从HTML中抽取文本

+

今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!

+`,r:{minutes:.19,words:58},y:"a",t:"Claude AI应用案例,从HTML中抽取文本",O:-16929216e5},[":md"]],["v-76f251d2","/daily/copy-code-may-not-be-guilty.html",{d:16955136e5,l:"2023年9月24日",g:["Daily"],e:`

复制代码也许不是罪

+

前言

+

熟悉我的人都知道,我对代码是有追求的。

+

正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。

+

我早期认为:复制代码就是菜。

+

后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

+

而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

+

编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

+`,r:{minutes:1.51,words:453},y:"a",t:"复制代码也许不是罪",O:-16955136e5},[":md"]],["v-f47f129e","/daily/dont-try-to-argue-with-a-sb.html",{d:16911072e5,l:"2023年8月4日",g:["Daily","Emotion","Working Experience","Video"],e:`

不要与傻逼进行争吵

+

这是个职场吐槽帖,也是个自我反思、情绪管理帖。

+

我在沟通上,有以下可以改正的点:
+0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你

+
    +
  1. 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你
  2. +
  3. 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通
  4. +
  5. 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答
  6. +
  7. 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!
  8. +
+`,r:{minutes:3.92,words:1176},y:"a",t:"不要与傻逼进行争吵",O:-16911072e5},[":md"]],["v-fd7b7f92","/daily/iteration-retrospective-of-sanyuan.html",{d:16941312e5,l:"2023年9月8日",g:["Daily","Working Experience"],e:`

迭代复盘之三员管理

+

前言

+

本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。

+

这次迭代因为各种原因,延期了快一个星期(周六还加了班)。

+

那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。

+`,r:{minutes:2.52,words:756},y:"a",t:"迭代复盘之三员管理",O:-16941312e5},[":md"]],["v-81a71778","/daily/leverage-ai-to-boost-coding-productivity.html",{d:1693008e6,l:"2023年8月26日",g:["AI","Daily"],e:`

都什么年代了,还在用传统方式写代码?

+

前言

+

还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

+

本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

+

本文将分享 AI 时代的编程新实践,教你如何从一个 "Ctrl + C"、 "Ctrl + V" 工程师,变成一个 "Tab + Enter" 工程师🤣。

`,r:{minutes:7.37,words:2212},y:"a",t:"都什么年代了,还在用传统方式写代码?",O:-1693008e6},[":md"]],["v-5c48d497","/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html",{d:1694304e6,l:"2023年9月10日",g:["Daily","Video"],e:`

微软中国CTO演讲观后感

+

看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。

+`,r:{minutes:.17,words:51},y:"a",t:"微软中国CTO演讲观后感",O:-1694304e6},[":md"]],["v-4f919602","/daily/testing-environments-should-be-consistent-with-production-environments.html",{d:16997472e5,l:"2023年11月12日",g:["Daily"],e:`

生产教训:测试环境要与生产环境一致

+

事件还原

+

业务流程:

+
    +
  1. app-a 上传文件
  2. +
  3. app-b 下载文件后使用文件
  4. +
+

其他信息:

+
    +
  1. 开发、测试环境使用 MinIO
  2. +
  3. 生产环境使用 Amazon S3
  4. +
+

问题:

+
    +
  1. app-a 上传文件成功
  2. +
  3. app-b 使用文件报错
  4. +
+

逐步分析定位问题:

+
    +
  1. app-a 与 app-b 配置是否一致?——确认都是使用 S3
  2. +
  3. S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题
  4. +
  5. app-a 是否真的上传成功?——确认文件已在 S3
  6. +
  7. app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。
  8. +
`,r:{minutes:2.49,words:748},y:"a",t:"生产教训:测试环境要与生产环境一致",O:-16997472e5},[":md"]],["v-1e305501","/daily/things-I-have-to-vent-about-vue.html",{d:16906752e5,l:"2023年7月30日",g:["Frontend","Daily","Video"],e:`

对Vue不得不吐槽的事

+

为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!

+

总结一下,我对Vue生态不满的地方在于:

+
    +
  1. Vue总是破坏性升级,新技术完全不管旧用户的体验
  2. +
  3. 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性
  4. +
+`,r:{minutes:.42,words:126},y:"a",t:"对Vue不得不吐槽的事",O:-16906752e5},[":md"]],["v-404740fa","/daily/use-claude2-instead-of-chatgpt.html",{d:16914528e5,l:"2023年8月8日",g:["Video","AI","Tool"],e:`

再见ChatGPT,我选择Claude2!

+

大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。

+

首先要评测的当然是ChatGPT了,因为最早用的就是它。

+`,r:{minutes:3.76,words:1128},y:"a",t:"再见ChatGPT,我选择Claude2!",O:-16914528e5},[":md"]],["v-f1efc11c","/daily/vim-creator-pass-away.html",{d:169128e7,l:"2023年8月6日",g:["Daily","Video"],e:`

Vim 作者离世

+

R.I.P 🙏

+

Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。

+

这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。

+`,r:{minutes:.24,words:71},y:"a",t:"Vim 作者离世",O:-169128e7},[":md"]],["v-414fd2b2","/daily/what-is-the-difference-between-sh-and-bash.html",{d:16910208e5,l:"2023年8月3日",g:["Linux","Docker","Video"],e:`

sh与bash的区别

+

结论:如果可移植性很重要,那么应该使用 sh!

+`,r:{minutes:.83,words:248},y:"a",t:"sh与bash的区别",O:-16910208e5},[":md"]],["v-bd2b5fe8","/daily/you-dont-need-to-add-tenant_id-to-every-table.html",{d:16949952e5,l:"2023年9月18日",g:["Design","Daily"],e:`

技术点评:别每张表都加tenant_id

+

前言

+

系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。

+`,r:{minutes:3.26,words:977},y:"a",t:"技术点评:别每张表都加tenant_id",O:-16949952e5},[":md"]],["v-971cc7fe","/english/contemporary-college-english-1.html",{d:16537824e5,l:"2022年5月29日",g:"English",e:`

现代大学英语精读(第2版)第一册

+

介绍

+

全书链接:https://www.ximalaya.com/album/43891910

+

虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。

+

虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。

`,r:{minutes:1.38,words:414},y:"a",t:"现代大学英语精读(第2版)第一册",O:-16537824e5},[":md"]],["v-93b316c0","/english/contemporary-college-english-2.html",{d:16543008e5,l:"2022年6月4日",g:"English",e:`

现代大学英语精读(第2版)第二册

+

介绍

+

全书链接:https://www.ximalaya.com/album/44290107

+

本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。

+

值得一读的文章

+

say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂

`,r:{minutes:1.96,words:588},y:"a",t:"现代大学英语精读(第2版)第二册",O:-16543008e5},[":md"]],["v-90496582","/english/contemporary-college-english-3.html",{d:16561152e5,l:"2022年6月25日",g:"English",e:`

现代大学英语精读(第2版)第三册

+

介绍

+

全书链接:https://www.ximalaya.com/album/44439108

+

阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。

`,r:{minutes:1.95,words:586},y:"a",t:"现代大学英语精读(第2版)第三册",O:-16561152e5},[":md"]],["v-8cdfb444","/english/contemporary-college-english-4.html",{d:16574976e5,l:"2022年7月11日",g:"English",e:`

现代大学英语精读(第2版)第四册

+

介绍

+

全书链接:https://www.ximalaya.com/album/44641280

+

本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。

+

值得一读的文章

+

Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从

`,r:{minutes:2.02,words:607},y:"a",t:"现代大学英语精读(第2版)第四册",O:-16574976e5},[":md"]],["v-89760306","/english/contemporary-college-english-5.html",{d:16616448e5,l:"2022年8月28日",g:"English",e:`

现代大学英语精读(第2版)第五册

+

介绍

+

全书链接:https://www.ximalaya.com/album/49466046

+

本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。

+

Who Are You and What Are You Doing Here

`,r:{minutes:7.18,words:2155},y:"a",t:"现代大学英语精读(第2版)第五册",O:-16616448e5},[":md"]],["v-860c51c8","/english/contemporary-college-english-6.html",{d:16622496e5,l:"2022年9月4日",g:"English",e:`

现代大学英语精读(第2版)第六册

+

前言

+

全书链接:https://www.ximalaya.com/album/49468954

+

本册是整个系列的最后一册了,完结撒花🎉

+

Paper Tigers

+

原文链接:https://www.ximalaya.com/sound/414998175

`,r:{minutes:5.08,words:1524},y:"a",t:"现代大学英语精读(第2版)第六册",O:-16622496e5},[":md"]],["v-246f4f17","/english/everyone-can-learn-english-1-overview.html",{d:16559424e5,l:"2022年6月23日",g:"English",e:`

人人都能学会的英语1:开篇

+

为什么学

+

不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。

+

也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语:

+
    +
  • 出国
  • +
  • 去外企
  • +
  • 有国际化需求
  • +
  • 学习专业领域的前沿知识
  • +
+

我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。

`,r:{minutes:4.46,words:1337},y:"a",t:"人人都能学会的英语1:开篇",O:-16559424e5},[":md"]],["v-3ac0474c","/english/everyone-can-learn-english-2-pronunciation.html",{d:16562016e5,l:"2022年6月26日",g:"English",e:`

人人都能学会的英语2:音标

+

方法

+

音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。

+

我推荐根据赖世雄的《美语音标》进行学习:

+
    +
  • 在微信公众号 常春藤英语集团 买相关书籍
  • +
  • 在 喜马拉雅 或 B站 听相关音频,进行跟读练习
  • +
  • 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格
  • +
+

有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。

`,r:{minutes:3.61,words:1083},y:"a",t:"人人都能学会的英语2:音标",O:-16562016e5},[":md"]],["v-58a409d7","/english/everyone-can-learn-english-3-words.html",{d:1656288e6,l:"2022年6月27日",g:"English",e:`

人人都能学会的英语3:单词

+

方法

+

单词是听说读写的基础,是有必要花时间去学习的。

+

然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议:

+
    +
  1. 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握
  2. +
  3. 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。
  4. +
  5. 只推荐利用词根记忆(当然并非百分百有效)
  6. +
  7. 学习软件:欧路词典——免费,全平台通用
  8. +
`,r:{minutes:4,words:1200},y:"a",t:"人人都能学会的英语3:单词",O:-1656288e6},[":md"]],["v-788d194a","/english/everyone-can-learn-english-4-listening-and-speaking.html",{d:16563744e5,l:"2022年6月28日",g:"English",e:`

人人都能学会的英语4:听说

+

前言

+

听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。

+

方法

+

听说属于综合能力,建议遵循以下学习步骤:

+
    +
  1. 激发兴趣,建立信心
  2. +
  3. 明确方向
  4. +
  5. 脚踏实地,坚持输入并输出
  6. +
+

1.建立信心

+

首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料:

+`,r:{minutes:3.82,words:1146},y:"a",t:"人人都能学会的英语4:听说",O:-16563744e5},[":md"]],["v-ae153a4e","/english/everyone-can-learn-english-5-reading-and-writing.html",{d:16564608e5,l:"2022年6月29日",g:"English",e:`

人人都能学会的英语5:读写

+

前言

+

读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。

+

阅读能力升级之旅

+

我先给出自己经历过的全英阅读能力的变化过程,仅供参考:

+
    +
  1. 看到英文网站,第一反应是点击切换中文版
  2. +
  3. 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读
  4. +
  5. 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域)
  6. +
  7. 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读
  8. +
  9. 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读
  10. +
  11. 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了
  12. +
`,r:{minutes:6.15,words:1846},y:"a",t:"人人都能学会的英语5:读写",O:-16564608e5},[":md"]],["v-d42db13c","/english/how-to-self-evaluate-english-level.html",{d:16573248e5,l:"2022年7月9日",g:"English",e:`

英文能力评测手把手教学

+

前言

+

之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。

+

单词量

+

进入网站:https://preply.com/en/learn/english/test-your-vocab

+

看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。
+image.png
+在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。
+image.png
+对于该结果,网站有以下值得注意的解释:

`,r:{minutes:1.85,words:554},y:"a",t:"英文能力评测手把手教学",O:-16573248e5},[":md"]],["v-6ed7d996","/english/learning-7000-words-task-completed.html",{d:16633728e5,l:"2022年9月17日",g:"English",e:`

完成刷7k单词任务

+

image.png
+这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。

+

首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。

`,r:{minutes:4.54,words:1362},y:"a",t:"完成刷7k单词任务",O:-16633728e5},[":md"]],["v-221efd1f","/english/let-chatgpt-be-your-foreign-language-teacher.html",{d:16833312e5,l:"2023年5月6日",g:["English","AI"],e:`

让 ChatGPT 成为你的外语私教

+

前言

+

有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。

+

本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。

+

准备工作

+

在开始之前,要准备好几样东西:

+
    +
  1. ChatGPT, 如果没有账号或不能上网,请查看上网教程
  2. +
  3. Chrome 浏览器插件 voice-control-for-chatgpt
  4. +
  5. 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明
  6. +
`,r:{minutes:2.83,words:849},y:"a",t:"让 ChatGPT 成为你的外语私教",O:-16833312e5},[":md"]],["v-72e84a92","/frontend/old-articles.html",{d:15674688e5,l:"2019年9月3日",g:"Frontend",e:`

旧文章精选

+`,r:{minutes:.3,words:90},y:"a",t:"旧文章精选",O:-15674688e5},[":md"]],["v-7320140c","/frontend/performance-optimization-in-action.html",{d:16117056e5,l:"2021年1月27日",g:"Frontend",e:`

前端项目性能优化实战

+

本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。

+`,r:{minutes:4.69,words:1407},y:"a",t:"前端项目性能优化实战",O:-16117056e5},[":md"]],["v-1e5872c4","/git/git-best-pratices.html",{d:16006464e5,l:"2020年9月21日",g:"Git",e:`

2020-09-21

+

Git最佳实践

+

精简提交

+

一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。
+如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。

+

频繁提交

+

一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。
+经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。

+

此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。

`,r:{minutes:3.86,words:1158},y:"a",t:"Git最佳实践",O:-16006464e5},[":md"]],["v-48e494ef","/git/git-definitive-guide-to-merge-code.html",{d:1651104e6,l:"2022年4月28日",g:["Git"],e:`

Git代码合并指南

+

前言

+

合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。
+在说明问题前,先定义一些概念:

+
    +
  • feat:指代功能分支
  • +
  • dev 与 test:指代两条不同的长驻分支,它们具有以下特点: +
      +
    • 受保护,不能直接推送
    • +
    • 不会被删除
    • +
    • 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test
    • +
    +
  • +
  • MR:merge request。代码合并请求
  • +
`,r:{minutes:3.45,words:1035},y:"a",t:"Git代码合并指南",O:-1651104e6},[":md"]],["v-60021cbc","/git/git-history-two-tricks-in-idea.html",{d:1691712e6,l:"2023年8月11日",g:["Git"],e:`

Git查看历史记录小技巧

+

分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。

+

这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。

+`,r:{minutes:2.16,words:648},y:"a",t:"Git查看历史记录小技巧",O:-1691712e6},[":md"]],["v-642eaaea","/git/git-useful-commands.html",{d:16006464e5,l:"2020年9月21日",g:"Git",e:`

Git常用命令

+

前言

+

本文将列举Git常见场景,并给出相应解决方案。

+

约定: 下文代码块中\${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。

+

推荐: 图形化交互式Git教程

+

配置

+

Mac/Linux 用户 执行以下操作

+
vi ~/.gitconfig
+
`,r:{minutes:5.05,words:1515},y:"a",t:"Git常用命令",O:-16006464e5},[":md"]],["v-4008ff77","/git/gitlab-ci.html",{d:16733088e5,l:"2023年1月10日",g:["Git","GitLab","Java","Node.js"],e:`

GitLab CI

+

前言

+

GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。

+`,r:{minutes:6.49,words:1946},y:"a",t:"GitLab CI",O:-16733088e5},[":md"]],["v-0fbf5fdc","/git/rethinking-git-flow.html",{d:16504992e5,l:"2022年4月21日",g:"Git",e:`

再论Git Flow

+

背景

+

团队目前使用的 Git 协作模式是:

+
    +
  1. 对每个功能建立相应的 feat 分支
  2. +
  3. 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat
  4. +
  5. 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支
  6. +
+

这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:

`,r:{minutes:5.53,words:1660},y:"a",t:"再论Git Flow",O:-16504992e5},[":md"]],["v-071be141","/git/use-command-line-tool-to-manage-gitlab-merge-request.html",{d:16795296e5,l:"2023年3月23日",g:["Git","GitLab","Python"],e:`

操作 Gitlab MR 的命令行工具

+

背景

+

为什么开发这个工具?主要解决以下问题:

+
    +
  1. 提测、上 UAT 时,避免漏合代码。
  2. +
  3. 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?”
  4. +
  5. 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。
  6. +
+

可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。
+并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。
+对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。

`,r:{minutes:4.42,words:1325},y:"a",t:"操作 Gitlab MR 的命令行工具",O:-16795296e5},[":md"]],["v-86a15cb6","/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html",{d:16693344e5,l:"2022年11月25日",g:["Java","Daily"],e:`

IDEA常见问题与解决方案

+

启动参数过长

+

Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun.
+解决方案:

+
    +
  1. 编辑 .idea/workspace.xml
  2. +
  3. 找到 PropertiesComponent
  4. +
  5. 添加:
  6. +
+

或者这样:
+image.png

`,r:{minutes:2.67,words:801},y:"a",t:"IDEA常见问题与解决方案",O:-16693344e5},[":md"]],["v-6dc18ec6","/java/Resolving-Common-Problems-in-Maven.md.html",{d:1670544e6,l:"2022年12月9日",g:["Java","Daily"],e:`

Maven常见问题与解决方案

+

运行 class 找不到主类

+
maven compile
+

得到 class 文件后

+
cd /my-app/target/com/mycompany/app
+java App
+
`,r:{minutes:3.28,words:984},y:"a",t:"Maven常见问题与解决方案",O:-1670544e6},[":md"]],["v-538a98d7","/java/avoid-sending-password-in-plaintext.html",{d:16981056e5,l:"2023年10月24日",g:["Java","JavaScript","Daily"],e:`

避免密码明文传输

+

说明

+

密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。

+

本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。

+

流程说明:前端加密,后端解密。

+

当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。

+`,r:{minutes:1.63,words:488},y:"a",t:"避免密码明文传输",O:-16981056e5},[":md"]],["v-2f6ae09c","/java/check-if-name-exists.html",{d:16975872e5,l:"2023年10月18日",g:["Java","MySQL","Daily"],e:`

检查名字是否重复

+

检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。

+`,r:{minutes:1.17,words:351},y:"a",t:"检查名字是否重复",O:-16975872e5},[":md"]],["v-b5a78a7a","/java/common-practices-for-handling-excel.html",{d:1693872e6,l:"2023年9月5日",g:["Java","Daily"],e:`

Excel处理常用实践

+

基础知识

+

导入需要用到对象,MultipartFile。

+
@PostMapping("/import")
+public boolean importLicense(
+      @RequestParam("file") MultipartFile file,
+      @RequestParam("tenantId") @NotBlank String tenantId,
+) {
+  return true;
+}
+
`,r:{minutes:4.82,words:1447},y:"a",t:"Excel处理常用实践",O:-1693872e6},[":md"]],["v-100d1814","/java/how-to-convert-snapshot-into-release-jar-without-source-code.html",{d:16917984e5,l:"2023年8月12日",g:["Java"],e:`

奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包

+

背景

+

项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。

+

问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。

+`,r:{minutes:1.41,words:422},y:"a",t:"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包",O:-16917984e5},[":md"]],["v-d767e98e","/java/recommend-practices-for-collections-naming-convention.html",{d:16540416e5,l:"2022年6月1日",g:["Java","Daily"],e:`

集合命名推荐

+

概述

+

建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。

+

当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章?
+image.png

+

这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。

`,r:{minutes:3.02,words:906},y:"a",t:"集合命名推荐",O:-16540416e5},[":md"]],["v-f5471cb0","/java/recommend-practices-for-query-by-date-range.html",{d:16944768e5,l:"2023年9月12日",g:["Java","Daily"],e:`

根据时间范围查询推荐实践

+

背景

+

不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。

+

虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。

+

本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson

+`,r:{minutes:3.21,words:962},y:"a",t:"根据时间范围查询推荐实践",O:-16944768e5},[":md"]],["v-57d9b582","/java/recommend-practices-for-writing-good-functions.html",{d:16657056e5,l:"2022年10月14日",g:["Java","Daily"],e:`

编写函数的最佳实践

+

前言

+

编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。

+

本文将推荐一些编写函数的最佳实践,以供参数。

+

减少重复

+

这是在遵守 Don't repeat yourself (DRY) 原则。

+

实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。

`,r:{minutes:5.88,words:1764},y:"a",t:"编写函数的最佳实践",O:-16657056e5},[":md"]],["v-2ba0b01e","/java/using-enum-in-java.html",{d:16657056e5,l:"2022年10月14日",g:["Java","Daily"],e:`

枚举的推荐实践

+

背景

+

定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。

+

而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。

+

本文推荐,不要在枚举中定义数字,直接使用枚举名即可!

+

Java

+

极简实现

+

理想状态下,枚举就应该这样简单!

+
public enum SEX {
+  MALE,
+  FEMALE;
+}
+
`,r:{minutes:5.59,words:1676},y:"a",t:"枚举的推荐实践",O:-16657056e5},[":md"]],["v-386e94f1","/java/which-one-is-better-Boolean-or-boolean.html",{d:1700647196e3,e:`

Boolean 还是 boolean?

+

在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢?

+

结论

+

先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。

+

原文如下:
+image.png

`,r:{minutes:3.5,words:1051},y:"a",t:"Boolean 还是 boolean?",O:null},[":md"]],["v-0d6828c2","/java/which-one-is-better-forEach-or-map.html",{d:1700647196e3,e:`

forEach 还是 map?

+

背景

+

遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有:

+
List<Type> result = new ArrayList<>();
+list.forEach(src -> {
+    Type target = BeanUtils.copyProperties(src, target);
+    //省略代码
+    result.add(target);
+});
+
`,r:{minutes:3.64,words:1091},y:"a",t:"forEach 还是 map?",O:null},[":md"]],["v-1aafac08","/java/why-i-prefer-fastjson-instead-of-jackson.html",{d:1692576e6,l:"2023年8月21日",g:["Java","Daily","Video"],e:`

Jackson 经典异常 UnrecognizedPropertyException

+

原因是 json 包含的字段,多于 Java 实体类定义的字段。

+

解决方法很简单:

+
new ObjectMapper()
+  .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
+

或者为相关实体添加注解:

+
@JsonIgnoreProperties(ignoreUnknown = true)
+public class ObjectParseFromJsonString {  }
+
`,r:{minutes:.36,words:109},y:"a",t:"Jackson 经典异常 UnrecognizedPropertyException",O:-1692576e6},[":md"]],["v-ca672354","/java/why-is-it-so-hard-to-upgrade-dependencies.html",{d:1693008e6,l:"2023年8月26日",g:["Java","Daily","Video"],e:`

升个jar版本,怎么这么难?

+

前言

+

无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。

+

通常来说,对 Web 应用而言,前端是消费者,后端是提供者。

+

今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?

+`,r:{minutes:1.8,words:540},y:"a",t:"升个jar版本,怎么这么难?",O:-1693008e6},[":md"]],["v-143a8bce","/mysql/mysql-backup-case-study-mysqldump-in-action.html",{d:16922304e5,l:"2023年8月17日",g:["Daily","MySQL"],e:`

数据备份案例:mysqldump实战

+

背景

+

前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。

+

并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。

+

在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。

+`,r:{minutes:4.1,words:1231},y:"a",t:"数据备份案例:mysqldump实战",O:-16922304e5},[":md"]],["v-7e2c7a0c","/mysql/mysql-data-migration-case-study-add-auto-increment.html",{d:1692144e6,l:"2023年8月16日",g:["Daily","MySQL"],e:`

数据迁移案例:表AUTO_INCREMENT加10w

+

背景

+

项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。

+

问题分析:

+
    +
  1. 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。
  2. +
  3. 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。
  4. +
+

迁移思路:

+
    +
  1. 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条)
  2. +
  3. mysql-b 的相应的表 AUTO_INCREMENT 加 10w
  4. +
  5. 记得动手前先确保数据已备份
  6. +
+

注意:

+
    +
  • mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。
  • +
  • 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。
  • +
+`,r:{minutes:2,words:599},y:"a",t:"数据迁移案例:表AUTO_INCREMENT加10w",O:-1692144e6},[":md"]],["v-f297935a","/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html",{d:16924032e5,l:"2023年8月19日",g:["Daily","MySQL"],e:`

MySQL 命令行执行SQL的细节

+

背景

+

经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了!

+

但要小心,在正式环境中执行 SQL,也许会有意想不到的坑!

+

环境说明

+

先说明下我们的环境信息。
+
+我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。

`,r:{minutes:3.02,words:906},y:"a",t:"MySQL 命令行执行SQL的细节",O:-16924032e5},[":md"]],["v-5b37b3c6","/python/export-mysql-table-into-excel.html",{d:16779744e5,l:"2023年3月5日",g:"Python",e:`

Python 导出 MySQL 库表信息到 Excel

+

需求

+

查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。

+`,r:{minutes:1.47,words:440},y:"a",t:"Python 导出 MySQL 库表信息到 Excel",O:-16779744e5},[":md"]],["v-113531b4","/software-testing/unit-testing-overview.html",{d:16905024e5,l:"2023年7月28日",g:["Testing"],e:`

单元测试概述

+

Why

+

为什么要做单元测试?或者说,为什么要写测试代码?

+

个人总结为以下两点:

+
    +
  1. 测试左移,降低修复bug的成本
    +
  2. +
  3. 形成资产,方便回归测试,后续迭代重构、维护有保障
  4. +
+`,r:{minutes:2.97,words:892},y:"a",t:"单元测试概述",O:-16905024e5},[":md"]],["v-65b23736","/software-testing/use-RestAssured-for-api-testing.html",{d:16862688e5,l:"2023年6月9日",g:["Java","Testing"],e:`

使用 RestAssured 进行 API 测试

+

前言

+

本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。

+

本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。

+

What

+

什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。

+

Why

+

为什么要做 API 测试呢?

`,r:{minutes:6.99,words:2096},y:"a",t:"使用 RestAssured 进行 API 测试",O:-16862688e5},[":md"]],["v-c488ac58","/software-testing/use-cypress-for-e2e-testing.html",{d:16073856e5,l:"2020年12月8日",g:["Node.js","Testing"],e:`

使用 Cypress 进行端对端测试

+

为什么写端对端测试

+

写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。

+

谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。

+

为什么用 Cypress

+

文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypress

`,r:{minutes:8.04,words:2413},y:"a",t:"使用 Cypress 进行端对端测试",O:-16073856e5},[":md"]],["v-efcacba2","/software-testing/use-jest-for-test-driven-development.html",{d:15558048e5,l:"2019年4月21日",g:["Node.js","Testing"],e:`

使用 Jest 实践测试驱动开发

+

前言

+

本文将使用jest进行测试驱动开发的示例,源码在github
+旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。

`,r:{minutes:6.26,words:1879},y:"a",t:"使用 Jest 实践测试驱动开发",O:-15558048e5},[":md"]],["v-0be1af08","/software-testing/use-playwright-for-ui-testing.html",{d:16834176e5,l:"2023年5月7日",g:["Node.js","Python","Testing"],e:`

下一代 UI 自动化测试工具 Playwright

+

前言

+

Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有:

+
    +
  1. 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具
  2. +
  3. 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件)
  4. +
+

另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。

`,r:{minutes:10,words:3001},y:"a",t:"下一代 UI 自动化测试工具 Playwright",O:-16834176e5},[":md"]],["v-75616b85","/software-testing/use-postman-for-api-testing.html",{d:16945632e5,l:"2023年9月13日",g:["Node.js","Daily"],e:`

使用 Postman 进行 API 测试

+

前言

+

虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。

+

而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。

+

还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:

+`,r:{minutes:5.51,words:1653},y:"a",t:"使用 Postman 进行 API 测试",O:-16945632e5},[":md"]],["v-17809471","/tools/how-to-connect-to-internet.html",{d:16556832e5,l:"2022年6月20日",g:"Tool",e:`

科学上网

+

说明

+

AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍?

+

本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。

+

购买指南

+

点击进入服务页面, 看到如下页面:
+image.png

`,r:{minutes:2.72,words:815},y:"a",t:"科学上网",O:-16556832e5},[":md"]],["v-3706649a","/404.html",{y:"p",t:"",O:null},[]],["v-79c9f96f","/daily/",{y:"p",t:"Daily"},[]],["v-43539db8","/english/",{y:"p",t:"English"},[]],["v-06198984","/frontend/",{y:"p",t:"Frontend"},[]],["v-74473916","/git/",{y:"p",t:"Git"},[]],["v-14c69af4","/java/",{y:"p",t:"Java"},[]],["v-eb072ff4","/mysql/",{y:"p",t:"Mysql"},[]],["v-63cd5dba","/python/",{y:"p",t:"Python"},[]],["v-0df55bac","/software-testing/",{y:"p",t:"Software Testing"},[]],["v-d440f426","/tools/",{y:"p",t:"Tools"},[]],["v-5bc93818","/category/",{y:"p",t:"分类",I:0},[]],["v-744d024e","/tag/",{y:"p",t:"标签",I:0},[]],["v-e52c881c","/article/",{y:"p",t:"文章",I:0},[]],["v-154dc4c4","/star/",{y:"p",t:"收藏",I:0},[]],["v-01560935","/timeline/",{y:"p",t:"时间轴",I:0},[]],["v-3d5315f8","/tag/daily/",{y:"p",t:"标签: Daily",I:0},[]],["v-1b3ae9cf","/tag/frontend/",{y:"p",t:"标签: Frontend",I:0},[]],["v-007c0ae2","/tag/video/",{y:"p",t:"标签: Video",I:0},[]],["v-211f44ee","/tag/linux/",{y:"p",t:"标签: Linux",I:0},[]],["v-6106c001","/tag/docker/",{y:"p",t:"标签: Docker",I:0},[]],["v-1bee38ca","/tag/mysql/",{y:"p",t:"标签: MySQL",I:0},[]],["v-0da0abf9","/tag/ai/",{y:"p",t:"标签: AI",I:0},[]],["v-5a3e80fc","/tag/emotion/",{y:"p",t:"标签: Emotion",I:0},[]],["v-01c1de5b","/tag/working-experience/",{y:"p",t:"标签: Working Experience",I:0},[]],["v-29350809","/tag/tool/",{y:"p",t:"标签: Tool",I:0},[]],["v-50d6e023","/tag/design/",{y:"p",t:"标签: Design",I:0},[]],["v-0ca0efe6","/tag/english/",{y:"p",t:"标签: English",I:0},[]],["v-b310d42a","/tag/git/",{y:"p",t:"标签: Git",I:0},[]],["v-13275df4","/tag/gitlab/",{y:"p",t:"标签: GitLab",I:0},[]],["v-28a1d8bf","/tag/java/",{y:"p",t:"标签: Java",I:0},[]],["v-60379330","/tag/node.js/",{y:"p",t:"标签: Node.js",I:0},[]],["v-245f5676","/tag/python/",{y:"p",t:"标签: Python",I:0},[]],["v-3b951558","/tag/javascript/",{y:"p",t:"标签: JavaScript",I:0},[]],["v-48b3e46d","/tag/testing/",{y:"p",t:"标签: Testing",I:0},[]]];var Ts=M({name:"Vuepress",setup(){const e=$0();return()=>s(e.value)}}),W2=()=>G2.reduce((e,[t,l,n,a])=>(e.push({name:t,path:l,component:Ts,meta:n},{path:l.endsWith("/")?l+"index.html":l.substring(0,l.length-5),redirect:l},...a.map(i=>({path:i===":md"?l.substring(0,l.length-5)+".md":i,redirect:l}))),e),[{name:"404",path:"/:catchAll(.*)",component:Ts}]),K2=nh,J2=()=>{const e=jh({history:K2(Li("/")),routes:W2(),scrollBehavior:(t,l,n)=>n||(t.hash?{el:t.hash}:{top:0})});return e.beforeResolve(async(t,l)=>{var n;(t.path!==l.path||l===Et)&&([Dt.value]=await Promise.all([wt.resolvePageData(t.name),(n=Oo[t.name])==null?void 0:n.__asyncLoader()]))}),e},Q2=e=>{e.component("ClientOnly",Zn),e.component("Content",No)},Y2=(e,t,l)=>{const n=E(()=>wt.resolveLayouts(l)),a=es(()=>t.currentRoute.value.path),i=es(()=>wt.resolveRouteLocale(ol.value.locales,a.value)),r=E(()=>wt.resolveSiteLocaleData(ol.value,i.value)),o=E(()=>wt.resolvePageFrontmatter(Dt.value)),c=E(()=>wt.resolvePageHeadTitle(Dt.value,r.value)),u=E(()=>wt.resolvePageHead(c.value,o.value,r.value)),d=E(()=>wt.resolvePageLang(Dt.value,r.value)),p=E(()=>wt.resolvePageLayout(Dt.value,n.value));return e.provide(C0,n),e.provide(Ro,o),e.provide(D0,c),e.provide(So,u),e.provide(Do,d),e.provide(Mo,p),e.provide(Ti,i),e.provide(Fo,r),Object.defineProperties(e.config.globalProperties,{$frontmatter:{get:()=>o.value},$head:{get:()=>u.value},$headTitle:{get:()=>c.value},$lang:{get:()=>d.value},$page:{get:()=>Dt.value},$routeLocale:{get:()=>i.value},$site:{get:()=>ol.value},$siteLocale:{get:()=>r.value},$withBase:{get:()=>Le}}),{layouts:n,pageData:Dt,pageFrontmatter:o,pageHead:u,pageHeadTitle:c,pageLang:d,pageLayout:p,routeLocale:i,siteData:ol,siteLocaleData:r}},X2=()=>{const e=S0(),t=$o(),l=W([]),n=()=>{e.value.forEach(i=>{const r=Z2(i);r&&l.value.push(r)})},a=()=>{document.documentElement.lang=t.value,l.value.forEach(i=>{i.parentNode===document.head&&document.head.removeChild(i)}),l.value.splice(0,l.value.length),e.value.forEach(i=>{const r=eg(i);r!==null&&(document.head.appendChild(r),l.value.push(r))})};ot(M0,a),ke(()=>{n(),a(),ce(()=>e.value,a)})},Z2=([e,t,l=""])=>{const n=Object.entries(t).map(([o,c])=>ae(c)?`[${o}=${JSON.stringify(c)}]`:c===!0?`[${o}]`:"").join(""),a=`head > ${e}${n}`;return Array.from(document.querySelectorAll(a)).find(o=>o.innerText===l)||null},eg=([e,t,l])=>{if(!ae(e))return null;const n=document.createElement(e);return xi(t)&&Object.entries(t).forEach(([a,i])=>{ae(i)?n.setAttribute(a,i):i===!0&&n.setAttribute(a,"")}),ae(l)&&n.appendChild(document.createTextNode(l)),n},tg=_0,lg=async()=>{var l;const e=tg({name:"VuepressApp",setup(){var n;X2();for(const a of Cn)(n=a.setup)==null||n.call(a);return()=>[s(Qo),...Cn.flatMap(({rootComponents:a=[]})=>a.map(i=>s(i)))]}}),t=J2();Q2(e),Y2(e,t,Cn);for(const n of Cn)await((l=n.enhance)==null?void 0:l.call(n,{app:e,router:t,siteData:ol}));return e.use(t),{app:e,router:t}};lg().then(({app:e,router:t})=>{t.isReady().then(()=>{e.mount("#app")})});export{mo as a,yo as b,ag as c,lg as createVueApp,Ae as d,rg as e,ig as f,Ip as o,Je as r,qd as w}; diff --git a/assets/app-b649ee34.js b/assets/app-b649ee34.js deleted file mode 100644 index 3309c393..00000000 --- a/assets/app-b649ee34.js +++ /dev/null @@ -1,377 +0,0 @@ -var Ru=Object.defineProperty;var Su=(e,t,l)=>t in e?Ru(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l;var oa=(e,t,l)=>(Su(e,typeof t!="symbol"?t+"":t,l),l);const Du="modulepreload",$u=function(e){return"/"+e},Xi={},y=function(t,l,n){if(!l||l.length===0)return t();const a=document.getElementsByTagName("link");return Promise.all(l.map(i=>{if(i=$u(i),i in Xi)return;Xi[i]=!0;const r=i.endsWith(".css"),o=r?'[rel="stylesheet"]':"";if(!!n)for(let d=a.length-1;d>=0;d--){const p=a[d];if(p.href===i&&(!r||p.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${i}"]${o}`))return;const u=document.createElement("link");if(u.rel=r?"stylesheet":Du,r||(u.as="script",u.crossOrigin=""),u.href=i,document.head.appendChild(u),r)return new Promise((d,p)=>{u.addEventListener("load",d),u.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${i}`)))})})).then(()=>t()).catch(i=>{const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=i,window.dispatchEvent(r),!r.defaultPrevented)throw i})};function ei(e,t){const l=Object.create(null),n=e.split(",");for(let a=0;a!!l[a.toLowerCase()]:a=>!!l[a]}const xe={},cl=[],st=()=>{},Mu=()=>!1,Vu=/^on[^a-z]/,Zl=e=>Vu.test(e),ti=e=>e.startsWith("onUpdate:"),Ie=Object.assign,li=(e,t)=>{const l=e.indexOf(t);l>-1&&e.splice(l,1)},Fu=Object.prototype.hasOwnProperty,oe=(e,t)=>Fu.call(e,t),X=Array.isArray,Dl=e=>Un(e)==="[object Map]",Nu=e=>Un(e)==="[object Set]",te=e=>typeof e=="function",ae=e=>typeof e=="string",ni=e=>typeof e=="symbol",Te=e=>e!==null&&typeof e=="object",As=e=>Te(e)&&te(e.then)&&te(e.catch),ju=Object.prototype.toString,Un=e=>ju.call(e),Bu=e=>Un(e).slice(8,-1),zu=e=>Un(e)==="[object Object]",ai=e=>ae(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,$l=ei(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),Gn=e=>{const t=Object.create(null);return l=>t[l]||(t[l]=e(l))},Hu=/-(\w)/g,tt=Gn(e=>e.replace(Hu,(t,l)=>l?l.toUpperCase():"")),qu=/\B([A-Z])/g,wl=Gn(e=>e.replace(qu,"-$1").toLowerCase()),en=Gn(e=>e.charAt(0).toUpperCase()+e.slice(1)),ca=Gn(e=>e?`on${en(e)}`:""),zl=(e,t)=>!Object.is(e,t),ua=(e,t)=>{for(let l=0;l{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:l})},Uu=e=>{const t=parseFloat(e);return isNaN(t)?e:t},Gu=e=>{const t=ae(e)?Number(e):NaN;return isNaN(t)?e:t};let Zi;const Oa=()=>Zi||(Zi=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function ii(e){if(X(e)){const t={};for(let l=0;l{if(l){const n=l.split(Ku);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}function ri(e){let t="";if(ae(e))t=e;else if(X(e))for(let l=0;l{const t=new Set(e);return t.w=0,t.n=0,t},Os=e=>(e.w&Nt)>0,Cs=e=>(e.n&Nt)>0,ld=({deps:e})=>{if(e.length)for(let t=0;t{const{deps:t}=e;if(t.length){let l=0;for(let n=0;n{(d==="length"||d>=c)&&o.push(u)})}else switch(l!==void 0&&o.push(r.get(l)),t){case"add":X(e)?ai(l)&&o.push(r.get("length")):(o.push(r.get(Jt)),Dl(e)&&o.push(r.get(Ra)));break;case"delete":X(e)||(o.push(r.get(Jt)),Dl(e)&&o.push(r.get(Ra)));break;case"set":Dl(e)&&o.push(r.get(Jt));break}if(o.length===1)o[0]&&Sa(o[0]);else{const c=[];for(const u of o)u&&c.push(...u);Sa(si(c))}}function Sa(e,t){const l=X(e)?e:[...e];for(const n of l)n.computed&&tr(n);for(const n of l)n.computed||tr(n)}function tr(e,t){(e!==at||e.allowRecurse)&&(e.scheduler?e.scheduler():e.run())}function ad(e,t){var l;return(l=Dn.get(e))==null?void 0:l.get(t)}const id=ei("__proto__,__v_isRef,__isVue"),Ds=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(ni)),rd=ci(),sd=ci(!1,!0),od=ci(!0),lr=cd();function cd(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...l){const n=re(this);for(let i=0,r=this.length;i{e[t]=function(...l){El();const n=re(this)[t].apply(this,l);return xl(),n}}),e}function ud(e){const t=re(this);return qe(t,"has",e),t.hasOwnProperty(e)}function ci(e=!1,t=!1){return function(n,a,i){if(a==="__v_isReactive")return!e;if(a==="__v_isReadonly")return e;if(a==="__v_isShallow")return t;if(a==="__v_raw"&&i===(e?t?Td:Ns:t?Fs:Vs).get(n))return n;const r=X(n);if(!e){if(r&&oe(lr,a))return Reflect.get(lr,a,i);if(a==="hasOwnProperty")return ud}const o=Reflect.get(n,a,i);return(ni(a)?Ds.has(a):id(a))||(e||qe(n,"get",a),t)?o:Oe(o)?r&&ai(a)?o:o.value:Te(o)?e?Yt(o):tn(o):o}}const dd=$s(),pd=$s(!0);function $s(e=!1){return function(l,n,a,i){let r=l[n];if(fl(r)&&Oe(r)&&!Oe(a))return!1;if(!e&&(!$n(a)&&!fl(a)&&(r=re(r),a=re(a)),!X(l)&&Oe(r)&&!Oe(a)))return r.value=a,!0;const o=X(l)&&ai(n)?Number(n)e,Wn=e=>Reflect.getPrototypeOf(e);function yn(e,t,l=!1,n=!1){e=e.__v_raw;const a=re(e),i=re(t);l||(t!==i&&qe(a,"get",t),qe(a,"get",i));const{has:r}=Wn(a),o=n?ui:l?hi:Hl;if(r.call(a,t))return o(e.get(t));if(r.call(a,i))return o(e.get(i));e!==a&&e.get(t)}function bn(e,t=!1){const l=this.__v_raw,n=re(l),a=re(e);return t||(e!==a&&qe(n,"has",e),qe(n,"has",a)),e===a?l.has(e):l.has(e)||l.has(a)}function _n(e,t=!1){return e=e.__v_raw,!t&&qe(re(e),"iterate",Jt),Reflect.get(e,"size",e)}function nr(e){e=re(e);const t=re(this);return Wn(t).has.call(t,e)||(t.add(e),Lt(t,"add",e,e)),this}function ar(e,t){t=re(t);const l=re(this),{has:n,get:a}=Wn(l);let i=n.call(l,e);i||(e=re(e),i=n.call(l,e));const r=a.call(l,e);return l.set(e,t),i?zl(t,r)&&Lt(l,"set",e,t):Lt(l,"add",e,t),this}function ir(e){const t=re(this),{has:l,get:n}=Wn(t);let a=l.call(t,e);a||(e=re(e),a=l.call(t,e)),n&&n.call(t,e);const i=t.delete(e);return a&&Lt(t,"delete",e,void 0),i}function rr(){const e=re(this),t=e.size!==0,l=e.clear();return t&&Lt(e,"clear",void 0,void 0),l}function kn(e,t){return function(n,a){const i=this,r=i.__v_raw,o=re(r),c=t?ui:e?hi:Hl;return!e&&qe(o,"iterate",Jt),r.forEach((u,d)=>n.call(a,c(u),c(d),i))}}function wn(e,t,l){return function(...n){const a=this.__v_raw,i=re(a),r=Dl(i),o=e==="entries"||e===Symbol.iterator&&r,c=e==="keys"&&r,u=a[e](...n),d=l?ui:t?hi:Hl;return!t&&qe(i,"iterate",c?Ra:Jt),{next(){const{value:p,done:h}=u.next();return h?{value:p,done:h}:{value:o?[d(p[0]),d(p[1])]:d(p),done:h}},[Symbol.iterator](){return this}}}}function Pt(e){return function(...t){return e==="delete"?!1:this}}function yd(){const e={get(i){return yn(this,i)},get size(){return _n(this)},has:bn,add:nr,set:ar,delete:ir,clear:rr,forEach:kn(!1,!1)},t={get(i){return yn(this,i,!1,!0)},get size(){return _n(this)},has:bn,add:nr,set:ar,delete:ir,clear:rr,forEach:kn(!1,!0)},l={get(i){return yn(this,i,!0)},get size(){return _n(this,!0)},has(i){return bn.call(this,i,!0)},add:Pt("add"),set:Pt("set"),delete:Pt("delete"),clear:Pt("clear"),forEach:kn(!0,!1)},n={get(i){return yn(this,i,!0,!0)},get size(){return _n(this,!0)},has(i){return bn.call(this,i,!0)},add:Pt("add"),set:Pt("set"),delete:Pt("delete"),clear:Pt("clear"),forEach:kn(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(i=>{e[i]=wn(i,!1,!1),l[i]=wn(i,!0,!1),t[i]=wn(i,!1,!0),n[i]=wn(i,!0,!0)}),[e,l,t,n]}const[bd,_d,kd,wd]=yd();function di(e,t){const l=t?e?wd:kd:e?_d:bd;return(n,a,i)=>a==="__v_isReactive"?!e:a==="__v_isReadonly"?e:a==="__v_raw"?n:Reflect.get(oe(l,a)&&a in n?l:n,a,i)}const Ed={get:di(!1,!1)},xd={get:di(!1,!0)},Ld={get:di(!0,!1)},Vs=new WeakMap,Fs=new WeakMap,Ns=new WeakMap,Td=new WeakMap;function Ad(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function Id(e){return e.__v_skip||!Object.isExtensible(e)?0:Ad(Bu(e))}function tn(e){return fl(e)?e:pi(e,!1,Ms,Ed,Vs)}function js(e){return pi(e,!1,md,xd,Fs)}function Yt(e){return pi(e,!0,gd,Ld,Ns)}function pi(e,t,l,n,a){if(!Te(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const i=a.get(e);if(i)return i;const r=Id(e);if(r===0)return e;const o=new Proxy(e,r===2?n:l);return a.set(e,o),o}function ul(e){return fl(e)?ul(e.__v_raw):!!(e&&e.__v_isReactive)}function fl(e){return!!(e&&e.__v_isReadonly)}function $n(e){return!!(e&&e.__v_isShallow)}function Bs(e){return ul(e)||fl(e)}function re(e){const t=e&&e.__v_raw;return t?re(t):e}function zs(e){return Sn(e,"__v_skip",!0),e}const Hl=e=>Te(e)?tn(e):e,hi=e=>Te(e)?Yt(e):e;function fi(e){Mt&&at&&(e=re(e),Ss(e.dep||(e.dep=si())))}function vi(e,t){e=re(e);const l=e.dep;l&&Sa(l)}function Oe(e){return!!(e&&e.__v_isRef===!0)}function W(e){return Hs(e,!1)}function Ue(e){return Hs(e,!0)}function Hs(e,t){return Oe(e)?e:new Pd(e,t)}class Pd{constructor(t,l){this.__v_isShallow=l,this.dep=void 0,this.__v_isRef=!0,this._rawValue=l?t:re(t),this._value=l?t:Hl(t)}get value(){return fi(this),this._value}set value(t){const l=this.__v_isShallow||$n(t)||fl(t);t=l?t:re(t),zl(t,this._rawValue)&&(this._rawValue=t,this._value=l?t:Hl(t),vi(this))}}function it(e){return Oe(e)?e.value:e}const Od={get:(e,t,l)=>it(Reflect.get(e,t,l)),set:(e,t,l,n)=>{const a=e[t];return Oe(a)&&!Oe(l)?(a.value=l,!0):Reflect.set(e,t,l,n)}};function qs(e){return ul(e)?e:new Proxy(e,Od)}class Cd{constructor(t){this.dep=void 0,this.__v_isRef=!0;const{get:l,set:n}=t(()=>fi(this),()=>vi(this));this._get=l,this._set=n}get value(){return this._get()}set value(t){this._set(t)}}function Rd(e){return new Cd(e)}function Sd(e){const t=X(e)?new Array(e.length):{};for(const l in e)t[l]=Us(e,l);return t}class Dd{constructor(t,l,n){this._object=t,this._key=l,this._defaultValue=n,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return ad(re(this._object),this._key)}}class $d{constructor(t){this._getter=t,this.__v_isRef=!0,this.__v_isReadonly=!0}get value(){return this._getter()}}function Ll(e,t,l){return Oe(e)?e:te(e)?new $d(e):Te(e)&&arguments.length>1?Us(e,t,l):W(e)}function Us(e,t,l){const n=e[t];return Oe(n)?n:new Dd(e,t,l)}class Md{constructor(t,l,n,a){this._setter=l,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this._dirty=!0,this.effect=new oi(t,()=>{this._dirty||(this._dirty=!0,vi(this))}),this.effect.computed=this,this.effect.active=this._cacheable=!a,this.__v_isReadonly=n}get value(){const t=re(this);return fi(t),(t._dirty||!t._cacheable)&&(t._dirty=!1,t._value=t.effect.run()),t._value}set value(t){this._setter(t)}}function Vd(e,t,l=!1){let n,a;const i=te(e);return i?(n=e,a=st):(n=e.get,a=e.set),new Md(n,a,i||!a,l)}function Vt(e,t,l,n){let a;try{a=n?e(...n):e()}catch(i){ln(i,t,l)}return a}function Ze(e,t,l,n){if(te(e)){const i=Vt(e,t,l,n);return i&&As(i)&&i.catch(r=>{ln(r,t,l)}),i}const a=[];for(let i=0;i>>1;Ul($e[n])vt&&$e.splice(t,1)}function Bd(e){X(e)?dl.push(...e):(!xt||!xt.includes(e,e.allowRecurse?Gt+1:Gt))&&dl.push(e),Ws()}function sr(e,t=ql?vt+1:0){for(;t<$e.length;t++){const l=$e[t];l&&l.pre&&($e.splice(t,1),t--,l())}}function Mn(e){if(dl.length){const t=[...new Set(dl)];if(dl.length=0,xt){xt.push(...t);return}for(xt=t,xt.sort((l,n)=>Ul(l)-Ul(n)),Gt=0;Gte.id==null?1/0:e.id,zd=(e,t)=>{const l=Ul(e)-Ul(t);if(l===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return l};function Ks(e){Da=!1,ql=!0,$e.sort(zd);const t=st;try{for(vt=0;vt<$e.length;vt++){const l=$e[vt];l&&l.active!==!1&&Vt(l,null,14)}}finally{vt=0,$e.length=0,Mn(),ql=!1,gi=null,($e.length||dl.length)&&Ks()}}function Hd(e,t,...l){if(e.isUnmounted)return;const n=e.vnode.props||xe;let a=l;const i=t.startsWith("update:"),r=i&&t.slice(7);if(r&&r in n){const d=`${r==="modelValue"?"model":r}Modifiers`,{number:p,trim:h}=n[d]||xe;h&&(a=l.map(f=>ae(f)?f.trim():f)),p&&(a=l.map(Uu))}let o,c=n[o=ca(t)]||n[o=ca(tt(t))];!c&&i&&(c=n[o=ca(wl(t))]),c&&Ze(c,e,6,a);const u=n[o+"Once"];if(u){if(!e.emitted)e.emitted={};else if(e.emitted[o])return;e.emitted[o]=!0,Ze(u,e,6,a)}}function Js(e,t,l=!1){const n=t.emitsCache,a=n.get(e);if(a!==void 0)return a;const i=e.emits;let r={},o=!1;if(!te(e)){const c=u=>{const d=Js(u,t,!0);d&&(o=!0,Ie(r,d))};!l&&t.mixins.length&&t.mixins.forEach(c),e.extends&&c(e.extends),e.mixins&&e.mixins.forEach(c)}return!i&&!o?(Te(e)&&n.set(e,null),null):(X(i)?i.forEach(c=>r[c]=null):Ie(r,i),Te(e)&&n.set(e,r),r)}function Jn(e,t){return!e||!Zl(t)?!1:(t=t.slice(2).replace(/Once$/,""),oe(e,t[0].toLowerCase()+t.slice(1))||oe(e,wl(t))||oe(e,t))}let Xe=null,Qs=null;function Vn(e){const t=Xe;return Xe=e,Qs=e&&e.type.__scopeId||null,t}function qd(e,t=Xe,l){if(!t||e._n)return e;const n=(...a)=>{n._d&&br(-1);const i=Vn(t);let r;try{r=e(...a)}finally{Vn(i),n._d&&br(1)}return r};return n._n=!0,n._c=!0,n._d=!0,n}function da(e){const{type:t,vnode:l,proxy:n,withProxy:a,props:i,propsOptions:[r],slots:o,attrs:c,emit:u,render:d,renderCache:p,data:h,setupState:f,ctx:b,inheritAttrs:w}=e;let x,g;const _=Vn(e);try{if(l.shapeFlag&4){const O=a||n;x=nt(d.call(O,O,p,i,f,h,b)),g=c}else{const O=t;x=nt(O.length>1?O(i,{attrs:c,slots:o,emit:u}):O(i,null)),g=t.props?c:Ud(c)}}catch(O){Nl.length=0,ln(O,e,1),x=Ae(et)}let P=x;if(g&&w!==!1){const O=Object.keys(g),{shapeFlag:B}=P;O.length&&B&7&&(r&&O.some(ti)&&(g=Gd(g,r)),P=jt(P,g))}return l.dirs&&(P=jt(P),P.dirs=P.dirs?P.dirs.concat(l.dirs):l.dirs),l.transition&&(P.transition=l.transition),x=P,Vn(_),x}const Ud=e=>{let t;for(const l in e)(l==="class"||l==="style"||Zl(l))&&((t||(t={}))[l]=e[l]);return t},Gd=(e,t)=>{const l={};for(const n in e)(!ti(n)||!(n.slice(9)in t))&&(l[n]=e[n]);return l};function Wd(e,t,l){const{props:n,children:a,component:i}=e,{props:r,children:o,patchFlag:c}=t,u=i.emitsOptions;if(t.dirs||t.transition)return!0;if(l&&c>=0){if(c&1024)return!0;if(c&16)return n?or(n,r,u):!!r;if(c&8){const d=t.dynamicProps;for(let p=0;pe.__isSuspense;function Ys(e,t){t&&t.pendingBranch?X(e)?t.effects.push(...e):t.effects.push(e):Bd(e)}function Xs(e,t){return mi(e,null,t)}const En={};function ce(e,t,l){return mi(e,t,l)}function mi(e,t,{immediate:l,deep:n,flush:a,onTrack:i,onTrigger:r}=xe){var o;const c=Ps()===((o=Ce)==null?void 0:o.scope)?Ce:null;let u,d=!1,p=!1;if(Oe(e)?(u=()=>e.value,d=$n(e)):ul(e)?(u=()=>e,n=!0):X(e)?(p=!0,d=e.some(O=>ul(O)||$n(O)),u=()=>e.map(O=>{if(Oe(O))return O.value;if(ul(O))return sl(O);if(te(O))return Vt(O,c,2)})):te(e)?t?u=()=>Vt(e,c,2):u=()=>{if(!(c&&c.isUnmounted))return h&&h(),Ze(e,c,3,[f])}:u=st,t&&n){const O=u;u=()=>sl(O())}let h,f=O=>{h=_.onStop=()=>{Vt(O,c,4)}},b;if(ml)if(f=st,t?l&&Ze(t,c,3,[u(),p?[]:void 0,f]):u(),a==="sync"){const O=qp();b=O.__watcherHandles||(O.__watcherHandles=[])}else return st;let w=p?new Array(e.length).fill(En):En;const x=()=>{if(_.active)if(t){const O=_.run();(n||d||(p?O.some((B,C)=>zl(B,w[C])):zl(O,w)))&&(h&&h(),Ze(t,c,3,[O,w===En?void 0:p&&w[0]===En?[]:w,f]),w=O)}else _.run()};x.allowRecurse=!!t;let g;a==="sync"?g=x:a==="post"?g=()=>Be(x,c&&c.suspense):(x.pre=!0,c&&(x.id=c.uid),g=()=>Kn(x));const _=new oi(u,g);t?l?x():w=_.run():a==="post"?Be(_.run.bind(_),c&&c.suspense):_.run();const P=()=>{_.stop(),c&&c.scope&&li(c.scope.effects,_)};return b&&b.push(P),P}function Qd(e,t,l){const n=this.proxy,a=ae(e)?e.includes(".")?Zs(n,e):()=>n[e]:e.bind(n,n);let i;te(t)?i=t:(i=t.handler,l=t);const r=Ce;gl(this);const o=mi(a,i.bind(n),l);return r?gl(r):Qt(),o}function Zs(e,t){const l=t.split(".");return()=>{let n=e;for(let a=0;a{sl(l,t)});else if(zu(e))for(const l in e)sl(e[l],t);return e}function ft(e,t,l,n){const a=e.dirs,i=t&&t.dirs;for(let r=0;r{e.isMounted=!0}),Yn(()=>{e.isUnmounting=!0}),e}const Qe=[Function,Array],to={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Qe,onEnter:Qe,onAfterEnter:Qe,onEnterCancelled:Qe,onBeforeLeave:Qe,onLeave:Qe,onAfterLeave:Qe,onLeaveCancelled:Qe,onBeforeAppear:Qe,onAppear:Qe,onAfterAppear:Qe,onAppearCancelled:Qe},Yd={name:"BaseTransition",props:to,setup(e,{slots:t}){const l=Xt(),n=eo();let a;return()=>{const i=t.default&&yi(t.default(),!0);if(!i||!i.length)return;let r=i[0];if(i.length>1){for(const w of i)if(w.type!==et){r=w;break}}const o=re(e),{mode:c}=o;if(n.isLeaving)return pa(r);const u=cr(r);if(!u)return pa(r);const d=Gl(u,o,n,l);Wl(u,d);const p=l.subTree,h=p&&cr(p);let f=!1;const{getTransitionKey:b}=u.type;if(b){const w=b();a===void 0?a=w:w!==a&&(a=w,f=!0)}if(h&&h.type!==et&&(!Wt(u,h)||f)){const w=Gl(h,o,n,l);if(Wl(h,w),c==="out-in")return n.isLeaving=!0,w.afterLeave=()=>{n.isLeaving=!1,l.update.active!==!1&&l.update()},pa(r);c==="in-out"&&u.type!==et&&(w.delayLeave=(x,g,_)=>{const P=lo(n,h);P[String(h.key)]=h,x._leaveCb=()=>{g(),x._leaveCb=void 0,delete d.delayedLeave},d.delayedLeave=_})}return r}}},Xd=Yd;function lo(e,t){const{leavingVNodes:l}=e;let n=l.get(t.type);return n||(n=Object.create(null),l.set(t.type,n)),n}function Gl(e,t,l,n){const{appear:a,mode:i,persisted:r=!1,onBeforeEnter:o,onEnter:c,onAfterEnter:u,onEnterCancelled:d,onBeforeLeave:p,onLeave:h,onAfterLeave:f,onLeaveCancelled:b,onBeforeAppear:w,onAppear:x,onAfterAppear:g,onAppearCancelled:_}=t,P=String(e.key),O=lo(l,e),B=(T,q)=>{T&&Ze(T,n,9,q)},C=(T,q)=>{const Y=q[1];B(T,q),X(T)?T.every(ne=>ne.length<=1)&&Y():T.length<=1&&Y()},V={mode:i,persisted:r,beforeEnter(T){let q=o;if(!l.isMounted)if(a)q=w||o;else return;T._leaveCb&&T._leaveCb(!0);const Y=O[P];Y&&Wt(e,Y)&&Y.el._leaveCb&&Y.el._leaveCb(),B(q,[T])},enter(T){let q=c,Y=u,ne=d;if(!l.isMounted)if(a)q=x||c,Y=g||u,ne=_||d;else return;let H=!1;const ee=T._enterCb=G=>{H||(H=!0,G?B(ne,[T]):B(Y,[T]),V.delayedLeave&&V.delayedLeave(),T._enterCb=void 0)};q?C(q,[T,ee]):ee()},leave(T,q){const Y=String(e.key);if(T._enterCb&&T._enterCb(!0),l.isUnmounting)return q();B(p,[T]);let ne=!1;const H=T._leaveCb=ee=>{ne||(ne=!0,q(),ee?B(b,[T]):B(f,[T]),T._leaveCb=void 0,O[Y]===e&&delete O[Y])};O[Y]=e,h?C(h,[T,H]):H()},clone(T){return Gl(T,t,l,n)}};return V}function pa(e){if(nn(e))return e=jt(e),e.children=null,e}function cr(e){return nn(e)?e.children?e.children[0]:void 0:e}function Wl(e,t){e.shapeFlag&6&&e.component?Wl(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function yi(e,t=!1,l){let n=[],a=0;for(let i=0;i1)for(let i=0;iIe({name:e.name},t,{setup:e}))():e}const Ml=e=>!!e.type.__asyncLoader;function D(e){te(e)&&(e={loader:e});const{loader:t,loadingComponent:l,errorComponent:n,delay:a=200,timeout:i,suspensible:r=!0,onError:o}=e;let c=null,u,d=0;const p=()=>(d++,c=null,h()),h=()=>{let f;return c||(f=c=t().catch(b=>{if(b=b instanceof Error?b:new Error(String(b)),o)return new Promise((w,x)=>{o(b,()=>w(p()),()=>x(b),d+1)});throw b}).then(b=>f!==c&&c?c:(b&&(b.__esModule||b[Symbol.toStringTag]==="Module")&&(b=b.default),u=b,b)))};return M({name:"AsyncComponentWrapper",__asyncLoader:h,get __asyncResolved(){return u},setup(){const f=Ce;if(u)return()=>ha(u,f);const b=_=>{c=null,ln(_,f,13,!n)};if(r&&f.suspense||ml)return h().then(_=>()=>ha(_,f)).catch(_=>(b(_),()=>n?Ae(n,{error:_}):null));const w=W(!1),x=W(),g=W(!!a);return a&&setTimeout(()=>{g.value=!1},a),i!=null&&setTimeout(()=>{if(!w.value&&!x.value){const _=new Error(`Async component timed out after ${i}ms.`);b(_),x.value=_}},i),h().then(()=>{w.value=!0,f.parent&&nn(f.parent.vnode)&&Kn(f.parent.update)}).catch(_=>{b(_),x.value=_}),()=>{if(w.value&&u)return ha(u,f);if(x.value&&n)return Ae(n,{error:x.value});if(l&&!g.value)return Ae(l)}}})}function ha(e,t){const{ref:l,props:n,children:a,ce:i}=t.vnode,r=Ae(e,n,a);return r.ref=l,r.ce=i,delete t.vnode.ce,r}const nn=e=>e.type.__isKeepAlive;function Zd(e,t){no(e,"a",t)}function ep(e,t){no(e,"da",t)}function no(e,t,l=Ce){const n=e.__wdc||(e.__wdc=()=>{let a=l;for(;a;){if(a.isDeactivated)return;a=a.parent}return e()});if(Qn(t,n,l),l){let a=l.parent;for(;a&&a.parent;)nn(a.parent.vnode)&&tp(n,t,l,a),a=a.parent}}function tp(e,t,l,n){const a=Qn(t,e,n,!0);an(()=>{li(n[t],a)},l)}function Qn(e,t,l=Ce,n=!1){if(l){const a=l[e]||(l[e]=[]),i=t.__weh||(t.__weh=(...r)=>{if(l.isUnmounted)return;El(),gl(l);const o=Ze(t,l,e,r);return Qt(),xl(),o});return n?a.unshift(i):a.push(i),i}}const At=e=>(t,l=Ce)=>(!ml||e==="sp")&&Qn(e,(...n)=>t(...n),l),lp=At("bm"),ke=At("m"),np=At("bu"),ao=At("u"),Yn=At("bum"),an=At("um"),ap=At("sp"),ip=At("rtg"),rp=At("rtc");function sp(e,t=Ce){Qn("ec",e,t)}const io="components";function Je(e,t){return cp(io,e,!0,t)||e}const op=Symbol.for("v-ndc");function cp(e,t,l=!0,n=!1){const a=Xe||Ce;if(a){const i=a.type;if(e===io){const o=Bp(i,!1);if(o&&(o===t||o===tt(t)||o===en(tt(t))))return i}const r=ur(a[e]||i[e],t)||ur(a.appContext[e],t);return!r&&n?i:r}}function ur(e,t){return e&&(e[t]||e[tt(t)]||e[en(tt(t))])}const $a=e=>e?bo(e)?Ei(e)||e.proxy:$a(e.parent):null,Vl=Ie(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>$a(e.parent),$root:e=>$a(e.root),$emit:e=>e.emit,$options:e=>bi(e),$forceUpdate:e=>e.f||(e.f=()=>Kn(e.update)),$nextTick:e=>e.n||(e.n=Tl.bind(e.proxy)),$watch:e=>Qd.bind(e)}),fa=(e,t)=>e!==xe&&!e.__isScriptSetup&&oe(e,t),up={get({_:e},t){const{ctx:l,setupState:n,data:a,props:i,accessCache:r,type:o,appContext:c}=e;let u;if(t[0]!=="$"){const f=r[t];if(f!==void 0)switch(f){case 1:return n[t];case 2:return a[t];case 4:return l[t];case 3:return i[t]}else{if(fa(n,t))return r[t]=1,n[t];if(a!==xe&&oe(a,t))return r[t]=2,a[t];if((u=e.propsOptions[0])&&oe(u,t))return r[t]=3,i[t];if(l!==xe&&oe(l,t))return r[t]=4,l[t];Ma&&(r[t]=0)}}const d=Vl[t];let p,h;if(d)return t==="$attrs"&&qe(e,"get",t),d(e);if((p=o.__cssModules)&&(p=p[t]))return p;if(l!==xe&&oe(l,t))return r[t]=4,l[t];if(h=c.config.globalProperties,oe(h,t))return h[t]},set({_:e},t,l){const{data:n,setupState:a,ctx:i}=e;return fa(a,t)?(a[t]=l,!0):n!==xe&&oe(n,t)?(n[t]=l,!0):oe(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(i[t]=l,!0)},has({_:{data:e,setupState:t,accessCache:l,ctx:n,appContext:a,propsOptions:i}},r){let o;return!!l[r]||e!==xe&&oe(e,r)||fa(t,r)||(o=i[0])&&oe(o,r)||oe(n,r)||oe(Vl,r)||oe(a.config.globalProperties,r)},defineProperty(e,t,l){return l.get!=null?e._.accessCache[t]=0:oe(l,"value")&&this.set(e,t,l.value,null),Reflect.defineProperty(e,t,l)}};function dr(e){return X(e)?e.reduce((t,l)=>(t[l]=null,t),{}):e}let Ma=!0;function dp(e){const t=bi(e),l=e.proxy,n=e.ctx;Ma=!1,t.beforeCreate&&pr(t.beforeCreate,e,"bc");const{data:a,computed:i,methods:r,watch:o,provide:c,inject:u,created:d,beforeMount:p,mounted:h,beforeUpdate:f,updated:b,activated:w,deactivated:x,beforeDestroy:g,beforeUnmount:_,destroyed:P,unmounted:O,render:B,renderTracked:C,renderTriggered:V,errorCaptured:T,serverPrefetch:q,expose:Y,inheritAttrs:ne,components:H,directives:ee,filters:G}=t;if(u&&pp(u,n,null),r)for(const _e in r){const fe=r[_e];te(fe)&&(n[_e]=fe.bind(l))}if(a){const _e=a.call(l,l);Te(_e)&&(e.data=tn(_e))}if(Ma=!0,i)for(const _e in i){const fe=i[_e],bt=te(fe)?fe.bind(l,l):te(fe.get)?fe.get.bind(l,l):st,It=!te(fe)&&te(fe.set)?fe.set.bind(l):st,pt=E({get:bt,set:It});Object.defineProperty(n,_e,{enumerable:!0,configurable:!0,get:()=>pt.value,set:je=>pt.value=je})}if(o)for(const _e in o)ro(o[_e],n,l,_e);if(c){const _e=te(c)?c.call(l):c;Reflect.ownKeys(_e).forEach(fe=>{ot(fe,_e[fe])})}d&&pr(d,e,"c");function he(_e,fe){X(fe)?fe.forEach(bt=>_e(bt.bind(l))):fe&&_e(fe.bind(l))}if(he(lp,p),he(ke,h),he(np,f),he(ao,b),he(Zd,w),he(ep,x),he(sp,T),he(rp,C),he(ip,V),he(Yn,_),he(an,O),he(ap,q),X(Y))if(Y.length){const _e=e.exposed||(e.exposed={});Y.forEach(fe=>{Object.defineProperty(_e,fe,{get:()=>l[fe],set:bt=>l[fe]=bt})})}else e.exposed||(e.exposed={});B&&e.render===st&&(e.render=B),ne!=null&&(e.inheritAttrs=ne),H&&(e.components=H),ee&&(e.directives=ee)}function pp(e,t,l=st){X(e)&&(e=Va(e));for(const n in e){const a=e[n];let i;Te(a)?"default"in a?i=me(a.from||n,a.default,!0):i=me(a.from||n):i=me(a),Oe(i)?Object.defineProperty(t,n,{enumerable:!0,configurable:!0,get:()=>i.value,set:r=>i.value=r}):t[n]=i}}function pr(e,t,l){Ze(X(e)?e.map(n=>n.bind(t.proxy)):e.bind(t.proxy),t,l)}function ro(e,t,l,n){const a=n.includes(".")?Zs(l,n):()=>l[n];if(ae(e)){const i=t[e];te(i)&&ce(a,i)}else if(te(e))ce(a,e.bind(l));else if(Te(e))if(X(e))e.forEach(i=>ro(i,t,l,n));else{const i=te(e.handler)?e.handler.bind(l):t[e.handler];te(i)&&ce(a,i,e)}}function bi(e){const t=e.type,{mixins:l,extends:n}=t,{mixins:a,optionsCache:i,config:{optionMergeStrategies:r}}=e.appContext,o=i.get(t);let c;return o?c=o:!a.length&&!l&&!n?c=t:(c={},a.length&&a.forEach(u=>Fn(c,u,r,!0)),Fn(c,t,r)),Te(t)&&i.set(t,c),c}function Fn(e,t,l,n=!1){const{mixins:a,extends:i}=t;i&&Fn(e,i,l,!0),a&&a.forEach(r=>Fn(e,r,l,!0));for(const r in t)if(!(n&&r==="expose")){const o=hp[r]||l&&l[r];e[r]=o?o(e[r],t[r]):t[r]}return e}const hp={data:hr,props:fr,emits:fr,methods:Sl,computed:Sl,beforeCreate:Fe,created:Fe,beforeMount:Fe,mounted:Fe,beforeUpdate:Fe,updated:Fe,beforeDestroy:Fe,beforeUnmount:Fe,destroyed:Fe,unmounted:Fe,activated:Fe,deactivated:Fe,errorCaptured:Fe,serverPrefetch:Fe,components:Sl,directives:Sl,watch:vp,provide:hr,inject:fp};function hr(e,t){return t?e?function(){return Ie(te(e)?e.call(this,this):e,te(t)?t.call(this,this):t)}:t:e}function fp(e,t){return Sl(Va(e),Va(t))}function Va(e){if(X(e)){const t={};for(let l=0;l1)return l&&te(t)?t.call(n&&n.proxy):t}}function yp(e,t,l,n=!1){const a={},i={};Sn(i,Xn,1),e.propsDefaults=Object.create(null),oo(e,t,a,i);for(const r in e.propsOptions[0])r in a||(a[r]=void 0);l?e.props=n?a:js(a):e.type.props?e.props=a:e.props=i,e.attrs=i}function bp(e,t,l,n){const{props:a,attrs:i,vnode:{patchFlag:r}}=e,o=re(a),[c]=e.propsOptions;let u=!1;if((n||r>0)&&!(r&16)){if(r&8){const d=e.vnode.dynamicProps;for(let p=0;p{c=!0;const[h,f]=co(p,t,!0);Ie(r,h),f&&o.push(...f)};!l&&t.mixins.length&&t.mixins.forEach(d),e.extends&&d(e.extends),e.mixins&&e.mixins.forEach(d)}if(!i&&!c)return Te(e)&&n.set(e,cl),cl;if(X(i))for(let d=0;d-1,f[1]=w<0||b-1||oe(f,"default"))&&o.push(p)}}}const u=[r,o];return Te(e)&&n.set(e,u),u}function vr(e){return e[0]!=="$"}function gr(e){const t=e&&e.toString().match(/^\s*(function|class) (\w+)/);return t?t[2]:e===null?"null":""}function mr(e,t){return gr(e)===gr(t)}function yr(e,t){return X(t)?t.findIndex(l=>mr(l,e)):te(t)&&mr(t,e)?0:-1}const uo=e=>e[0]==="_"||e==="$stable",_i=e=>X(e)?e.map(nt):[nt(e)],_p=(e,t,l)=>{if(t._n)return t;const n=qd((...a)=>_i(t(...a)),l);return n._c=!1,n},po=(e,t,l)=>{const n=e._ctx;for(const a in e){if(uo(a))continue;const i=e[a];if(te(i))t[a]=_p(a,i,n);else if(i!=null){const r=_i(i);t[a]=()=>r}}},ho=(e,t)=>{const l=_i(t);e.slots.default=()=>l},kp=(e,t)=>{if(e.vnode.shapeFlag&32){const l=t._;l?(e.slots=re(t),Sn(t,"_",l)):po(t,e.slots={})}else e.slots={},t&&ho(e,t);Sn(e.slots,Xn,1)},wp=(e,t,l)=>{const{vnode:n,slots:a}=e;let i=!0,r=xe;if(n.shapeFlag&32){const o=t._;o?l&&o===1?i=!1:(Ie(a,t),!l&&o===1&&delete a._):(i=!t.$stable,po(t,a)),r=t}else t&&(ho(e,t),r={default:1});if(i)for(const o in a)!uo(o)&&!(o in r)&&delete a[o]};function jn(e,t,l,n,a=!1){if(X(e)){e.forEach((h,f)=>jn(h,t&&(X(t)?t[f]:t),l,n,a));return}if(Ml(n)&&!a)return;const i=n.shapeFlag&4?Ei(n.component)||n.component.proxy:n.el,r=a?null:i,{i:o,r:c}=e,u=t&&t.r,d=o.refs===xe?o.refs={}:o.refs,p=o.setupState;if(u!=null&&u!==c&&(ae(u)?(d[u]=null,oe(p,u)&&(p[u]=null)):Oe(u)&&(u.value=null)),te(c))Vt(c,o,12,[r,d]);else{const h=ae(c),f=Oe(c);if(h||f){const b=()=>{if(e.f){const w=h?oe(p,c)?p[c]:d[c]:c.value;a?X(w)&&li(w,i):X(w)?w.includes(i)||w.push(i):h?(d[c]=[i],oe(p,c)&&(p[c]=d[c])):(c.value=[i],e.k&&(d[e.k]=c.value))}else h?(d[c]=r,oe(p,c)&&(p[c]=r)):f&&(c.value=r,e.k&&(d[e.k]=r))};r?(b.id=-1,Be(b,l)):b()}}}let Ot=!1;const xn=e=>/svg/.test(e.namespaceURI)&&e.tagName!=="foreignObject",Ln=e=>e.nodeType===8;function Ep(e){const{mt:t,p:l,o:{patchProp:n,createText:a,nextSibling:i,parentNode:r,remove:o,insert:c,createComment:u}}=e,d=(g,_)=>{if(!_.hasChildNodes()){l(null,g,_),Mn(),_._vnode=g;return}Ot=!1,p(_.firstChild,g,null,null,null),Mn(),_._vnode=g,Ot&&console.error("Hydration completed but contains mismatches.")},p=(g,_,P,O,B,C=!1)=>{const V=Ln(g)&&g.data==="[",T=()=>w(g,_,P,O,B,V),{type:q,ref:Y,shapeFlag:ne,patchFlag:H}=_;let ee=g.nodeType;_.el=g,H===-2&&(C=!1,_.dynamicChildren=null);let G=null;switch(q){case vl:ee!==3?_.children===""?(c(_.el=a(""),r(g),g),G=g):G=T():(g.data!==_.children&&(Ot=!0,g.data=_.children),G=i(g));break;case et:ee!==8||V?G=T():G=i(g);break;case Fl:if(V&&(g=i(g),ee=g.nodeType),ee===1||ee===3){G=g;const Se=!_.children.length;for(let he=0;he<_.staticCount;he++)Se&&(_.children+=G.nodeType===1?G.outerHTML:G.data),he===_.staticCount-1&&(_.anchor=G),G=i(G);return V?i(G):G}else T();break;case Ke:V?G=b(g,_,P,O,B,C):G=T();break;default:if(ne&1)ee!==1||_.type.toLowerCase()!==g.tagName.toLowerCase()?G=T():G=h(g,_,P,O,B,C);else if(ne&6){_.slotScopeIds=B;const Se=r(g);if(t(_,Se,null,P,O,xn(Se),C),G=V?x(g):i(g),G&&Ln(G)&&G.data==="teleport end"&&(G=i(G)),Ml(_)){let he;V?(he=Ae(Ke),he.anchor=G?G.previousSibling:Se.lastChild):he=g.nodeType===3?yo(""):Ae("div"),he.el=g,_.component.subTree=he}}else ne&64?ee!==8?G=T():G=_.type.hydrate(g,_,P,O,B,C,e,f):ne&128&&(G=_.type.hydrate(g,_,P,O,xn(r(g)),B,C,e,p))}return Y!=null&&jn(Y,null,O,_),G},h=(g,_,P,O,B,C)=>{C=C||!!_.dynamicChildren;const{type:V,props:T,patchFlag:q,shapeFlag:Y,dirs:ne}=_,H=V==="input"&&ne||V==="option";if(H||q!==-1){if(ne&&ft(_,null,P,"created"),T)if(H||!C||q&48)for(const G in T)(H&&G.endsWith("value")||Zl(G)&&!$l(G))&&n(g,G,null,T[G],!1,void 0,P);else T.onClick&&n(g,"onClick",null,T.onClick,!1,void 0,P);let ee;if((ee=T&&T.onVnodeBeforeMount)&&Ye(ee,P,_),ne&&ft(_,null,P,"beforeMount"),((ee=T&&T.onVnodeMounted)||ne)&&Ys(()=>{ee&&Ye(ee,P,_),ne&&ft(_,null,P,"mounted")},O),Y&16&&!(T&&(T.innerHTML||T.textContent))){let G=f(g.firstChild,_,g,P,O,B,C);for(;G;){Ot=!0;const Se=G;G=G.nextSibling,o(Se)}}else Y&8&&g.textContent!==_.children&&(Ot=!0,g.textContent=_.children)}return g.nextSibling},f=(g,_,P,O,B,C,V)=>{V=V||!!_.dynamicChildren;const T=_.children,q=T.length;for(let Y=0;Y{const{slotScopeIds:V}=_;V&&(B=B?B.concat(V):V);const T=r(g),q=f(i(g),_,T,P,O,B,C);return q&&Ln(q)&&q.data==="]"?i(_.anchor=q):(Ot=!0,c(_.anchor=u("]"),T,q),q)},w=(g,_,P,O,B,C)=>{if(Ot=!0,_.el=null,C){const q=x(g);for(;;){const Y=i(g);if(Y&&Y!==q)o(Y);else break}}const V=i(g),T=r(g);return o(g),l(null,_,T,V,P,O,xn(T),B),V},x=g=>{let _=0;for(;g;)if(g=i(g),g&&Ln(g)&&(g.data==="["&&_++,g.data==="]")){if(_===0)return i(g);_--}return g};return[d,p]}const Be=Ys;function xp(e){return Lp(e,Ep)}function Lp(e,t){const l=Oa();l.__VUE__=!0;const{insert:n,remove:a,patchProp:i,createElement:r,createText:o,createComment:c,setText:u,setElementText:d,parentNode:p,nextSibling:h,setScopeId:f=st,insertStaticContent:b}=e,w=(v,m,k,L=null,I=null,R=null,j=!1,$=null,F=!!m.dynamicChildren)=>{if(v===m)return;v&&!Wt(v,m)&&(L=A(v),je(v,I,R,!0),v=null),m.patchFlag===-2&&(F=!1,m.dynamicChildren=null);const{type:S,ref:J,shapeFlag:U}=m;switch(S){case vl:x(v,m,k,L);break;case et:g(v,m,k,L);break;case Fl:v==null&&_(m,k,L,j);break;case Ke:H(v,m,k,L,I,R,j,$,F);break;default:U&1?B(v,m,k,L,I,R,j,$,F):U&6?ee(v,m,k,L,I,R,j,$,F):(U&64||U&128)&&S.process(v,m,k,L,I,R,j,$,F,N)}J!=null&&I&&jn(J,v&&v.ref,R,m||v,!m)},x=(v,m,k,L)=>{if(v==null)n(m.el=o(m.children),k,L);else{const I=m.el=v.el;m.children!==v.children&&u(I,m.children)}},g=(v,m,k,L)=>{v==null?n(m.el=c(m.children||""),k,L):m.el=v.el},_=(v,m,k,L)=>{[v.el,v.anchor]=b(v.children,m,k,L,v.el,v.anchor)},P=({el:v,anchor:m},k,L)=>{let I;for(;v&&v!==m;)I=h(v),n(v,k,L),v=I;n(m,k,L)},O=({el:v,anchor:m})=>{let k;for(;v&&v!==m;)k=h(v),a(v),v=k;a(m)},B=(v,m,k,L,I,R,j,$,F)=>{j=j||m.type==="svg",v==null?C(m,k,L,I,R,j,$,F):q(v,m,I,R,j,$,F)},C=(v,m,k,L,I,R,j,$)=>{let F,S;const{type:J,props:U,shapeFlag:Q,transition:Z,dirs:le}=v;if(F=v.el=r(v.type,R,U&&U.is,U),Q&8?d(F,v.children):Q&16&&T(v.children,F,null,L,I,R&&J!=="foreignObject",j,$),le&&ft(v,null,L,"created"),V(F,v,v.scopeId,j,L),U){for(const ye in U)ye!=="value"&&!$l(ye)&&i(F,ye,null,U[ye],R,v.children,L,I,De);"value"in U&&i(F,"value",null,U.value),(S=U.onVnodeBeforeMount)&&Ye(S,L,v)}le&&ft(v,null,L,"beforeMount");const we=(!I||I&&!I.pendingBranch)&&Z&&!Z.persisted;we&&Z.beforeEnter(F),n(F,m,k),((S=U&&U.onVnodeMounted)||we||le)&&Be(()=>{S&&Ye(S,L,v),we&&Z.enter(F),le&&ft(v,null,L,"mounted")},I)},V=(v,m,k,L,I)=>{if(k&&f(v,k),L)for(let R=0;R{for(let S=F;S{const $=m.el=v.el;let{patchFlag:F,dynamicChildren:S,dirs:J}=m;F|=v.patchFlag&16;const U=v.props||xe,Q=m.props||xe;let Z;k&&qt(k,!1),(Z=Q.onVnodeBeforeUpdate)&&Ye(Z,k,m,v),J&&ft(m,v,k,"beforeUpdate"),k&&qt(k,!0);const le=I&&m.type!=="foreignObject";if(S?Y(v.dynamicChildren,S,$,k,L,le,R):j||fe(v,m,$,null,k,L,le,R,!1),F>0){if(F&16)ne($,m,U,Q,k,L,I);else if(F&2&&U.class!==Q.class&&i($,"class",null,Q.class,I),F&4&&i($,"style",U.style,Q.style,I),F&8){const we=m.dynamicProps;for(let ye=0;ye{Z&&Ye(Z,k,m,v),J&&ft(m,v,k,"updated")},L)},Y=(v,m,k,L,I,R,j)=>{for(let $=0;${if(k!==L){if(k!==xe)for(const $ in k)!$l($)&&!($ in L)&&i(v,$,k[$],null,j,m.children,I,R,De);for(const $ in L){if($l($))continue;const F=L[$],S=k[$];F!==S&&$!=="value"&&i(v,$,S,F,j,m.children,I,R,De)}"value"in L&&i(v,"value",k.value,L.value)}},H=(v,m,k,L,I,R,j,$,F)=>{const S=m.el=v?v.el:o(""),J=m.anchor=v?v.anchor:o("");let{patchFlag:U,dynamicChildren:Q,slotScopeIds:Z}=m;Z&&($=$?$.concat(Z):Z),v==null?(n(S,k,L),n(J,k,L),T(m.children,k,J,I,R,j,$,F)):U>0&&U&64&&Q&&v.dynamicChildren?(Y(v.dynamicChildren,Q,k,I,R,j,$),(m.key!=null||I&&m===I.subTree)&&fo(v,m,!0)):fe(v,m,k,J,I,R,j,$,F)},ee=(v,m,k,L,I,R,j,$,F)=>{m.slotScopeIds=$,v==null?m.shapeFlag&512?I.ctx.activate(m,k,L,j,F):G(m,k,L,I,R,j,F):Se(v,m,F)},G=(v,m,k,L,I,R,j)=>{const $=v.component=Mp(v,L,I);if(nn(v)&&($.ctx.renderer=N),Vp($),$.asyncDep){if(I&&I.registerDep($,he),!v.el){const F=$.subTree=Ae(et);g(null,F,m,k)}return}he($,v,m,k,I,R,j)},Se=(v,m,k)=>{const L=m.component=v.component;if(Wd(v,m,k))if(L.asyncDep&&!L.asyncResolved){_e(L,m,k);return}else L.next=m,jd(L.update),L.update();else m.el=v.el,L.vnode=m},he=(v,m,k,L,I,R,j)=>{const $=()=>{if(v.isMounted){let{next:J,bu:U,u:Q,parent:Z,vnode:le}=v,we=J,ye;qt(v,!1),J?(J.el=le.el,_e(v,J,j)):J=le,U&&ua(U),(ye=J.props&&J.props.onVnodeBeforeUpdate)&&Ye(ye,Z,J,le),qt(v,!0);const Pe=da(v),lt=v.subTree;v.subTree=Pe,w(lt,Pe,p(lt.el),A(lt),v,I,R),J.el=Pe.el,we===null&&Kd(v,Pe.el),Q&&Be(Q,I),(ye=J.props&&J.props.onVnodeUpdated)&&Be(()=>Ye(ye,Z,J,le),I)}else{let J;const{el:U,props:Q}=m,{bm:Z,m:le,parent:we}=v,ye=Ml(m);if(qt(v,!1),Z&&ua(Z),!ye&&(J=Q&&Q.onVnodeBeforeMount)&&Ye(J,we,m),qt(v,!0),U&&ve){const Pe=()=>{v.subTree=da(v),ve(U,v.subTree,v,I,null)};ye?m.type.__asyncLoader().then(()=>!v.isUnmounted&&Pe()):Pe()}else{const Pe=v.subTree=da(v);w(null,Pe,k,L,v,I,R),m.el=Pe.el}if(le&&Be(le,I),!ye&&(J=Q&&Q.onVnodeMounted)){const Pe=m;Be(()=>Ye(J,we,Pe),I)}(m.shapeFlag&256||we&&Ml(we.vnode)&&we.vnode.shapeFlag&256)&&v.a&&Be(v.a,I),v.isMounted=!0,m=k=L=null}},F=v.effect=new oi($,()=>Kn(S),v.scope),S=v.update=()=>F.run();S.id=v.uid,qt(v,!0),S()},_e=(v,m,k)=>{m.component=v;const L=v.vnode.props;v.vnode=m,v.next=null,bp(v,m.props,L,k),wp(v,m.children,k),El(),sr(),xl()},fe=(v,m,k,L,I,R,j,$,F=!1)=>{const S=v&&v.children,J=v?v.shapeFlag:0,U=m.children,{patchFlag:Q,shapeFlag:Z}=m;if(Q>0){if(Q&128){It(S,U,k,L,I,R,j,$,F);return}else if(Q&256){bt(S,U,k,L,I,R,j,$,F);return}}Z&8?(J&16&&De(S,I,R),U!==S&&d(k,U)):J&16?Z&16?It(S,U,k,L,I,R,j,$,F):De(S,I,R,!0):(J&8&&d(k,""),Z&16&&T(U,k,L,I,R,j,$,F))},bt=(v,m,k,L,I,R,j,$,F)=>{v=v||cl,m=m||cl;const S=v.length,J=m.length,U=Math.min(S,J);let Q;for(Q=0;QJ?De(v,I,R,!0,!1,U):T(m,k,L,I,R,j,$,F,U)},It=(v,m,k,L,I,R,j,$,F)=>{let S=0;const J=m.length;let U=v.length-1,Q=J-1;for(;S<=U&&S<=Q;){const Z=v[S],le=m[S]=F?St(m[S]):nt(m[S]);if(Wt(Z,le))w(Z,le,k,null,I,R,j,$,F);else break;S++}for(;S<=U&&S<=Q;){const Z=v[U],le=m[Q]=F?St(m[Q]):nt(m[Q]);if(Wt(Z,le))w(Z,le,k,null,I,R,j,$,F);else break;U--,Q--}if(S>U){if(S<=Q){const Z=Q+1,le=ZQ)for(;S<=U;)je(v[S],I,R,!0),S++;else{const Z=S,le=S,we=new Map;for(S=le;S<=Q;S++){const Ge=m[S]=F?St(m[S]):nt(m[S]);Ge.key!=null&&we.set(Ge.key,S)}let ye,Pe=0;const lt=Q-le+1;let nl=!1,Ji=0;const Pl=new Array(lt);for(S=0;S=lt){je(Ge,I,R,!0);continue}let ht;if(Ge.key!=null)ht=we.get(Ge.key);else for(ye=le;ye<=Q;ye++)if(Pl[ye-le]===0&&Wt(Ge,m[ye])){ht=ye;break}ht===void 0?je(Ge,I,R,!0):(Pl[ht-le]=S+1,ht>=Ji?Ji=ht:nl=!0,w(Ge,m[ht],k,null,I,R,j,$,F),Pe++)}const Qi=nl?Tp(Pl):cl;for(ye=Qi.length-1,S=lt-1;S>=0;S--){const Ge=le+S,ht=m[Ge],Yi=Ge+1{const{el:R,type:j,transition:$,children:F,shapeFlag:S}=v;if(S&6){pt(v.component.subTree,m,k,L);return}if(S&128){v.suspense.move(m,k,L);return}if(S&64){j.move(v,m,k,N);return}if(j===Ke){n(R,m,k);for(let U=0;U$.enter(R),I);else{const{leave:U,delayLeave:Q,afterLeave:Z}=$,le=()=>n(R,m,k),we=()=>{U(R,()=>{le(),Z&&Z()})};Q?Q(R,le,we):we()}else n(R,m,k)},je=(v,m,k,L=!1,I=!1)=>{const{type:R,props:j,ref:$,children:F,dynamicChildren:S,shapeFlag:J,patchFlag:U,dirs:Q}=v;if($!=null&&jn($,null,k,v,!0),J&256){m.ctx.deactivate(v);return}const Z=J&1&&Q,le=!Ml(v);let we;if(le&&(we=j&&j.onVnodeBeforeUnmount)&&Ye(we,m,v),J&6)mn(v.component,k,L);else{if(J&128){v.suspense.unmount(k,L);return}Z&&ft(v,null,m,"beforeUnmount"),J&64?v.type.remove(v,m,k,I,N,L):S&&(R!==Ke||U>0&&U&64)?De(S,m,k,!1,!0):(R===Ke&&U&384||!I&&J&16)&&De(F,m,k),L&&tl(v)}(le&&(we=j&&j.onVnodeUnmounted)||Z)&&Be(()=>{we&&Ye(we,m,v),Z&&ft(v,null,m,"unmounted")},k)},tl=v=>{const{type:m,el:k,anchor:L,transition:I}=v;if(m===Ke){ll(k,L);return}if(m===Fl){O(v);return}const R=()=>{a(k),I&&!I.persisted&&I.afterLeave&&I.afterLeave()};if(v.shapeFlag&1&&I&&!I.persisted){const{leave:j,delayLeave:$}=I,F=()=>j(k,R);$?$(v.el,R,F):F()}else R()},ll=(v,m)=>{let k;for(;v!==m;)k=h(v),a(v),v=k;a(m)},mn=(v,m,k)=>{const{bum:L,scope:I,update:R,subTree:j,um:$}=v;L&&ua(L),I.stop(),R&&(R.active=!1,je(j,v,m,k)),$&&Be($,m),Be(()=>{v.isUnmounted=!0},m),m&&m.pendingBranch&&!m.isUnmounted&&v.asyncDep&&!v.asyncResolved&&v.suspenseId===m.pendingId&&(m.deps--,m.deps===0&&m.resolve())},De=(v,m,k,L=!1,I=!1,R=0)=>{for(let j=R;jv.shapeFlag&6?A(v.component.subTree):v.shapeFlag&128?v.suspense.next():h(v.anchor||v.el),z=(v,m,k)=>{v==null?m._vnode&&je(m._vnode,null,null,!0):w(m._vnode||null,v,m,null,null,null,k),sr(),Mn(),m._vnode=v},N={p:w,um:je,m:pt,r:tl,mt:G,mc:T,pc:fe,pbc:Y,n:A,o:e};let K,ve;return t&&([K,ve]=t(N)),{render:z,hydrate:K,createApp:mp(z,K)}}function qt({effect:e,update:t},l){e.allowRecurse=t.allowRecurse=l}function fo(e,t,l=!1){const n=e.children,a=t.children;if(X(n)&&X(a))for(let i=0;i>1,e[l[o]]0&&(t[n]=l[i-1]),l[i]=n)}}for(i=l.length,r=l[i-1];i-- >0;)l[i]=r,r=t[r];return l}const Ap=e=>e.__isTeleport,Ke=Symbol.for("v-fgt"),vl=Symbol.for("v-txt"),et=Symbol.for("v-cmt"),Fl=Symbol.for("v-stc"),Nl=[];let rt=null;function Ip(e=!1){Nl.push(rt=e?null:[])}function Pp(){Nl.pop(),rt=Nl[Nl.length-1]||null}let Kl=1;function br(e){Kl+=e}function vo(e){return e.dynamicChildren=Kl>0?rt||cl:null,Pp(),Kl>0&&rt&&rt.push(e),e}function ag(e,t,l,n,a,i){return vo(mo(e,t,l,n,a,i,!0))}function Op(e,t,l,n,a){return vo(Ae(e,t,l,n,a,!0))}function Na(e){return e?e.__v_isVNode===!0:!1}function Wt(e,t){return e.type===t.type&&e.key===t.key}const Xn="__vInternal",go=({key:e})=>e??null,Rn=({ref:e,ref_key:t,ref_for:l})=>(typeof e=="number"&&(e=""+e),e!=null?ae(e)||Oe(e)||te(e)?{i:Xe,r:e,k:t,f:!!l}:e:null);function mo(e,t=null,l=null,n=0,a=null,i=e===Ke?0:1,r=!1,o=!1){const c={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&go(t),ref:t&&Rn(t),scopeId:Qs,slotScopeIds:null,children:l,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:i,patchFlag:n,dynamicProps:a,dynamicChildren:null,appContext:null,ctx:Xe};return o?(ki(c,l),i&128&&e.normalize(c)):l&&(c.shapeFlag|=ae(l)?8:16),Kl>0&&!r&&rt&&(c.patchFlag>0||i&6)&&c.patchFlag!==32&&rt.push(c),c}const Ae=Cp;function Cp(e,t=null,l=null,n=0,a=null,i=!1){if((!e||e===op)&&(e=et),Na(e)){const o=jt(e,t,!0);return l&&ki(o,l),Kl>0&&!i&&rt&&(o.shapeFlag&6?rt[rt.indexOf(e)]=o:rt.push(o)),o.patchFlag|=-2,o}if(zp(e)&&(e=e.__vccOpts),t){t=Rp(t);let{class:o,style:c}=t;o&&!ae(o)&&(t.class=ri(o)),Te(c)&&(Bs(c)&&!X(c)&&(c=Ie({},c)),t.style=ii(c))}const r=ae(e)?1:Jd(e)?128:Ap(e)?64:Te(e)?4:te(e)?2:0;return mo(e,t,l,n,a,r,i,!0)}function Rp(e){return e?Bs(e)||Xn in e?Ie({},e):e:null}function jt(e,t,l=!1){const{props:n,ref:a,patchFlag:i,children:r}=e,o=t?Sp(n||{},t):n;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:o,key:o&&go(o),ref:t&&t.ref?l&&a?X(a)?a.concat(Rn(t)):[a,Rn(t)]:Rn(t):a,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:r,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==Ke?i===-1?16:i|16:i,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&jt(e.ssContent),ssFallback:e.ssFallback&&jt(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce}}function yo(e=" ",t=0){return Ae(vl,null,e,t)}function ig(e,t){const l=Ae(Fl,null,e);return l.staticCount=t,l}function rg(e="",t=!1){return t?(Ip(),Op(et,null,e)):Ae(et,null,e)}function nt(e){return e==null||typeof e=="boolean"?Ae(et):X(e)?Ae(Ke,null,e.slice()):typeof e=="object"?St(e):Ae(vl,null,String(e))}function St(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:jt(e)}function ki(e,t){let l=0;const{shapeFlag:n}=e;if(t==null)t=null;else if(X(t))l=16;else if(typeof t=="object")if(n&65){const a=t.default;a&&(a._c&&(a._d=!1),ki(e,a()),a._c&&(a._d=!0));return}else{l=32;const a=t._;!a&&!(Xn in t)?t._ctx=Xe:a===3&&Xe&&(Xe.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else te(t)?(t={default:t,_ctx:Xe},l=32):(t=String(t),n&64?(l=16,t=[yo(t)]):l=8);e.children=t,e.shapeFlag|=l}function Sp(...e){const t={};for(let l=0;lCe||Xe;let wi,al,_r="__VUE_INSTANCE_SETTERS__";(al=Oa()[_r])||(al=Oa()[_r]=[]),al.push(e=>Ce=e),wi=e=>{al.length>1?al.forEach(t=>t(e)):al[0](e)};const gl=e=>{wi(e),e.scope.on()},Qt=()=>{Ce&&Ce.scope.off(),wi(null)};function bo(e){return e.vnode.shapeFlag&4}let ml=!1;function Vp(e,t=!1){ml=t;const{props:l,children:n}=e.vnode,a=bo(e);yp(e,l,a,t),kp(e,n);const i=a?Fp(e,t):void 0;return ml=!1,i}function Fp(e,t){const l=e.type;e.accessCache=Object.create(null),e.proxy=zs(new Proxy(e.ctx,up));const{setup:n}=l;if(n){const a=e.setupContext=n.length>1?jp(e):null;gl(e),El();const i=Vt(n,e,0,[e.props,a]);if(xl(),Qt(),As(i)){if(i.then(Qt,Qt),t)return i.then(r=>{kr(e,r,t)}).catch(r=>{ln(r,e,0)});e.asyncDep=i}else kr(e,i,t)}else _o(e,t)}function kr(e,t,l){te(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:Te(t)&&(e.setupState=qs(t)),_o(e,l)}let wr;function _o(e,t,l){const n=e.type;if(!e.render){if(!t&&wr&&!n.render){const a=n.template||bi(e).template;if(a){const{isCustomElement:i,compilerOptions:r}=e.appContext.config,{delimiters:o,compilerOptions:c}=n,u=Ie(Ie({isCustomElement:i,delimiters:o},r),c);n.render=wr(a,u)}}e.render=n.render||st}gl(e),El(),dp(e),xl(),Qt()}function Np(e){return e.attrsProxy||(e.attrsProxy=new Proxy(e.attrs,{get(t,l){return qe(e,"get","$attrs"),t[l]}}))}function jp(e){const t=l=>{e.exposed=l||{}};return{get attrs(){return Np(e)},slots:e.slots,emit:e.emit,expose:t}}function Ei(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(qs(zs(e.exposed)),{get(t,l){if(l in t)return t[l];if(l in Vl)return Vl[l](e)},has(t,l){return l in t||l in Vl}}))}function Bp(e,t=!0){return te(e)?e.displayName||e.name:e.name||t&&e.__name}function zp(e){return te(e)&&"__vccOpts"in e}const E=(e,t)=>Vd(e,t,ml);function s(e,t,l){const n=arguments.length;return n===2?Te(t)&&!X(t)?Na(t)?Ae(e,null,[t]):Ae(e,t):Ae(e,null,t):(n>3?l=Array.prototype.slice.call(arguments,2):n===3&&Na(l)&&(l=[l]),Ae(e,t,l))}const Hp=Symbol.for("v-scx"),qp=()=>me(Hp),Up="3.3.4",Gp="http://www.w3.org/2000/svg",Kt=typeof document<"u"?document:null,Er=Kt&&Kt.createElement("template"),Wp={insert:(e,t,l)=>{t.insertBefore(e,l||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,l,n)=>{const a=t?Kt.createElementNS(Gp,e):Kt.createElement(e,l?{is:l}:void 0);return e==="select"&&n&&n.multiple!=null&&a.setAttribute("multiple",n.multiple),a},createText:e=>Kt.createTextNode(e),createComment:e=>Kt.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Kt.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,l,n,a,i){const r=l?l.previousSibling:t.lastChild;if(a&&(a===i||a.nextSibling))for(;t.insertBefore(a.cloneNode(!0),l),!(a===i||!(a=a.nextSibling)););else{Er.innerHTML=n?`${e}`:e;const o=Er.content;if(n){const c=o.firstChild;for(;c.firstChild;)o.appendChild(c.firstChild);o.removeChild(c)}t.insertBefore(o,l)}return[r?r.nextSibling:t.firstChild,l?l.previousSibling:t.lastChild]}};function Kp(e,t,l){const n=e._vtc;n&&(t=(t?[t,...n]:[...n]).join(" ")),t==null?e.removeAttribute("class"):l?e.setAttribute("class",t):e.className=t}function Jp(e,t,l){const n=e.style,a=ae(l);if(l&&!a){if(t&&!ae(t))for(const i in t)l[i]==null&&ja(n,i,"");for(const i in l)ja(n,i,l[i])}else{const i=n.display;a?t!==l&&(n.cssText=l):t&&e.removeAttribute("style"),"_vod"in e&&(n.display=i)}}const xr=/\s*!important$/;function ja(e,t,l){if(X(l))l.forEach(n=>ja(e,t,n));else if(l==null&&(l=""),t.startsWith("--"))e.setProperty(t,l);else{const n=Qp(e,t);xr.test(l)?e.setProperty(wl(n),l.replace(xr,""),"important"):e[n]=l}}const Lr=["Webkit","Moz","ms"],va={};function Qp(e,t){const l=va[t];if(l)return l;let n=tt(t);if(n!=="filter"&&n in e)return va[t]=n;n=en(n);for(let a=0;aga||(n0.then(()=>ga=0),ga=Date.now());function i0(e,t){const l=n=>{if(!n._vts)n._vts=Date.now();else if(n._vts<=l.attached)return;Ze(r0(n,l.value),t,5,[n])};return l.value=e,l.attached=a0(),l}function r0(e,t){if(X(t)){const l=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{l.call(e),e._stopped=!0},t.map(n=>a=>!a._stopped&&n&&n(a))}else return t}const Ir=/^on[a-z]/,s0=(e,t,l,n,a=!1,i,r,o,c)=>{t==="class"?Kp(e,n,a):t==="style"?Jp(e,l,n):Zl(t)?ti(t)||t0(e,t,l,n,r):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):o0(e,t,n,a))?Xp(e,t,n,i,r,o,c):(t==="true-value"?e._trueValue=n:t==="false-value"&&(e._falseValue=n),Yp(e,t,n,a))};function o0(e,t,l,n){return n?!!(t==="innerHTML"||t==="textContent"||t in e&&Ir.test(t)&&te(l)):t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA"||Ir.test(t)&&ae(l)?!1:t in e}const Ct="transition",Ol="animation",Bt=(e,{slots:t})=>s(Xd,wo(e),t);Bt.displayName="Transition";const ko={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},c0=Bt.props=Ie({},to,ko),Ut=(e,t=[])=>{X(e)?e.forEach(l=>l(...t)):e&&e(...t)},Pr=e=>e?X(e)?e.some(t=>t.length>1):e.length>1:!1;function wo(e){const t={};for(const H in e)H in ko||(t[H]=e[H]);if(e.css===!1)return t;const{name:l="v",type:n,duration:a,enterFromClass:i=`${l}-enter-from`,enterActiveClass:r=`${l}-enter-active`,enterToClass:o=`${l}-enter-to`,appearFromClass:c=i,appearActiveClass:u=r,appearToClass:d=o,leaveFromClass:p=`${l}-leave-from`,leaveActiveClass:h=`${l}-leave-active`,leaveToClass:f=`${l}-leave-to`}=e,b=u0(a),w=b&&b[0],x=b&&b[1],{onBeforeEnter:g,onEnter:_,onEnterCancelled:P,onLeave:O,onLeaveCancelled:B,onBeforeAppear:C=g,onAppear:V=_,onAppearCancelled:T=P}=t,q=(H,ee,G)=>{Rt(H,ee?d:o),Rt(H,ee?u:r),G&&G()},Y=(H,ee)=>{H._isLeaving=!1,Rt(H,p),Rt(H,f),Rt(H,h),ee&&ee()},ne=H=>(ee,G)=>{const Se=H?V:_,he=()=>q(ee,H,G);Ut(Se,[ee,he]),Or(()=>{Rt(ee,H?c:i),kt(ee,H?d:o),Pr(Se)||Cr(ee,n,w,he)})};return Ie(t,{onBeforeEnter(H){Ut(g,[H]),kt(H,i),kt(H,r)},onBeforeAppear(H){Ut(C,[H]),kt(H,c),kt(H,u)},onEnter:ne(!1),onAppear:ne(!0),onLeave(H,ee){H._isLeaving=!0;const G=()=>Y(H,ee);kt(H,p),xo(),kt(H,h),Or(()=>{H._isLeaving&&(Rt(H,p),kt(H,f),Pr(O)||Cr(H,n,x,G))}),Ut(O,[H,G])},onEnterCancelled(H){q(H,!1),Ut(P,[H])},onAppearCancelled(H){q(H,!0),Ut(T,[H])},onLeaveCancelled(H){Y(H),Ut(B,[H])}})}function u0(e){if(e==null)return null;if(Te(e))return[ma(e.enter),ma(e.leave)];{const t=ma(e);return[t,t]}}function ma(e){return Gu(e)}function kt(e,t){t.split(/\s+/).forEach(l=>l&&e.classList.add(l)),(e._vtc||(e._vtc=new Set)).add(t)}function Rt(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.remove(n));const{_vtc:l}=e;l&&(l.delete(t),l.size||(e._vtc=void 0))}function Or(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let d0=0;function Cr(e,t,l,n){const a=e._endId=++d0,i=()=>{a===e._endId&&n()};if(l)return setTimeout(i,l);const{type:r,timeout:o,propCount:c}=Eo(e,t);if(!r)return n();const u=r+"end";let d=0;const p=()=>{e.removeEventListener(u,h),i()},h=f=>{f.target===e&&++d>=c&&p()};setTimeout(()=>{d(l[b]||"").split(", "),a=n(`${Ct}Delay`),i=n(`${Ct}Duration`),r=Rr(a,i),o=n(`${Ol}Delay`),c=n(`${Ol}Duration`),u=Rr(o,c);let d=null,p=0,h=0;t===Ct?r>0&&(d=Ct,p=r,h=i.length):t===Ol?u>0&&(d=Ol,p=u,h=c.length):(p=Math.max(r,u),d=p>0?r>u?Ct:Ol:null,h=d?d===Ct?i.length:c.length:0);const f=d===Ct&&/\b(transform|all)(,|$)/.test(n(`${Ct}Property`).toString());return{type:d,timeout:p,propCount:h,hasTransform:f}}function Rr(e,t){for(;e.lengthSr(l)+Sr(e[n])))}function Sr(e){return Number(e.slice(0,-1).replace(",","."))*1e3}function xo(){return document.body.offsetHeight}const Lo=new WeakMap,To=new WeakMap,Ao={name:"TransitionGroup",props:Ie({},c0,{tag:String,moveClass:String}),setup(e,{slots:t}){const l=Xt(),n=eo();let a,i;return ao(()=>{if(!a.length)return;const r=e.moveClass||`${e.name||"v"}-move`;if(!m0(a[0].el,l.vnode.el,r))return;a.forEach(f0),a.forEach(v0);const o=a.filter(g0);xo(),o.forEach(c=>{const u=c.el,d=u.style;kt(u,r),d.transform=d.webkitTransform=d.transitionDuration="";const p=u._moveCb=h=>{h&&h.target!==u||(!h||/transform$/.test(h.propertyName))&&(u.removeEventListener("transitionend",p),u._moveCb=null,Rt(u,r))};u.addEventListener("transitionend",p)})}),()=>{const r=re(e),o=wo(r);let c=r.tag||Ke;a=i,i=t.default?yi(t.default()):[];for(let u=0;udelete e.mode;Ao.props;const h0=Ao;function f0(e){const t=e.el;t._moveCb&&t._moveCb(),t._enterCb&&t._enterCb()}function v0(e){To.set(e,e.el.getBoundingClientRect())}function g0(e){const t=Lo.get(e),l=To.get(e),n=t.left-l.left,a=t.top-l.top;if(n||a){const i=e.el.style;return i.transform=i.webkitTransform=`translate(${n}px,${a}px)`,i.transitionDuration="0s",e}}function m0(e,t,l){const n=e.cloneNode();e._vtc&&e._vtc.forEach(r=>{r.split(/\s+/).forEach(o=>o&&n.classList.remove(o))}),l.split(/\s+/).forEach(r=>r&&n.classList.add(r)),n.style.display="none";const a=t.nodeType===1?t:t.parentNode;a.appendChild(n);const{hasTransform:i}=Eo(n);return a.removeChild(n),i}const y0=Ie({patchProp:s0},Wp);let ya,Dr=!1;function b0(){return ya=Dr?ya:xp(y0),Dr=!0,ya}const _0=(...e)=>{const t=b0().createApp(...e),{mount:l}=t;return t.mount=n=>{const a=k0(n);if(a)return l(a,!0,a instanceof SVGElement)},t};function k0(e){return ae(e)?document.querySelector(e):e}const w0={"v-8daa1a0e":()=>y(()=>import("./index.html-396a0c75.js"),[]).then(({data:e})=>e),"v-22a39d25":()=>y(()=>import("./about.html-177390a1.js"),[]).then(({data:e})=>e),"v-30a50b00":()=>y(()=>import("./a-vuepress2-entertaining-video.html-348ce561.js"),[]).then(({data:e})=>e),"v-0481cc80":()=>y(()=>import("./a-warning-from-navicat.html-159d00a7.js"),[]).then(({data:e})=>e),"v-31eac486":()=>y(()=>import("./about-arm-things-you-need-to-know.html-ce8055c2.js"),[]).then(({data:e})=>e),"v-79e139f8":()=>y(()=>import("./beyond-utf8-do-you-know-utf8mb4-and-collation.html-5673c414.js"),[]).then(({data:e})=>e),"v-7a74360a":()=>y(()=>import("./claude-ai-in-action-extract-info-from-html.html-2c1451df.js"),[]).then(({data:e})=>e),"v-76f251d2":()=>y(()=>import("./copy-code-may-not-be-guilty.html-765412c3.js"),[]).then(({data:e})=>e),"v-f47f129e":()=>y(()=>import("./dont-try-to-argue-with-a-sb.html-71bbf5fd.js"),[]).then(({data:e})=>e),"v-fd7b7f92":()=>y(()=>import("./iteration-retrospective-of-sanyuan.html-9a8c4533.js"),[]).then(({data:e})=>e),"v-81a71778":()=>y(()=>import("./leverage-ai-to-boost-coding-productivity.html-fac3783c.js"),[]).then(({data:e})=>e),"v-5c48d497":()=>y(()=>import("./reflections-on-a-speech-by-cto-of-microsoft-china.html-51d46984.js"),[]).then(({data:e})=>e),"v-1e305501":()=>y(()=>import("./things-I-have-to-vent-about-vue.html-38ebc534.js"),[]).then(({data:e})=>e),"v-404740fa":()=>y(()=>import("./use-claude2-instead-of-chatgpt.html-a143e23e.js"),[]).then(({data:e})=>e),"v-f1efc11c":()=>y(()=>import("./vim-creator-pass-away.html-1941f40c.js"),[]).then(({data:e})=>e),"v-414fd2b2":()=>y(()=>import("./what-is-the-difference-between-sh-and-bash.html-c3e619d0.js"),[]).then(({data:e})=>e),"v-bd2b5fe8":()=>y(()=>import("./you-dont-need-to-add-tenant_id-to-every-table.html-fadcd1b3.js"),[]).then(({data:e})=>e),"v-971cc7fe":()=>y(()=>import("./contemporary-college-english-1.html-6b25fa3f.js"),[]).then(({data:e})=>e),"v-93b316c0":()=>y(()=>import("./contemporary-college-english-2.html-de9e2dd5.js"),[]).then(({data:e})=>e),"v-90496582":()=>y(()=>import("./contemporary-college-english-3.html-9d16468c.js"),[]).then(({data:e})=>e),"v-8cdfb444":()=>y(()=>import("./contemporary-college-english-4.html-ab55347b.js"),[]).then(({data:e})=>e),"v-89760306":()=>y(()=>import("./contemporary-college-english-5.html-461e5519.js"),[]).then(({data:e})=>e),"v-860c51c8":()=>y(()=>import("./contemporary-college-english-6.html-b05e70fd.js"),[]).then(({data:e})=>e),"v-246f4f17":()=>y(()=>import("./everyone-can-learn-english-1-overview.html-f280cd0b.js"),[]).then(({data:e})=>e),"v-3ac0474c":()=>y(()=>import("./everyone-can-learn-english-2-pronunciation.html-3bc03470.js"),[]).then(({data:e})=>e),"v-58a409d7":()=>y(()=>import("./everyone-can-learn-english-3-words.html-6f098cd3.js"),[]).then(({data:e})=>e),"v-788d194a":()=>y(()=>import("./everyone-can-learn-english-4-listening-and-speaking.html-e5671ccf.js"),[]).then(({data:e})=>e),"v-ae153a4e":()=>y(()=>import("./everyone-can-learn-english-5-reading-and-writing.html-3815eb16.js"),[]).then(({data:e})=>e),"v-d42db13c":()=>y(()=>import("./how-to-self-evaluate-english-level.html-61f99657.js"),[]).then(({data:e})=>e),"v-6ed7d996":()=>y(()=>import("./learning-7000-words-task-completed.html-8232d487.js"),[]).then(({data:e})=>e),"v-221efd1f":()=>y(()=>import("./let-chatgpt-be-your-foreign-language-teacher.html-3ed89775.js"),[]).then(({data:e})=>e),"v-72e84a92":()=>y(()=>import("./old-articles.html-3e4d9bf0.js"),[]).then(({data:e})=>e),"v-7320140c":()=>y(()=>import("./performance-optimization-in-action.html-f6072ca8.js"),[]).then(({data:e})=>e),"v-1e5872c4":()=>y(()=>import("./git-best-pratices.html-4037825e.js"),[]).then(({data:e})=>e),"v-48e494ef":()=>y(()=>import("./git-definitive-guide-to-merge-code.html-06d295be.js"),[]).then(({data:e})=>e),"v-60021cbc":()=>y(()=>import("./git-history-two-tricks-in-idea.html-fd760def.js"),[]).then(({data:e})=>e),"v-642eaaea":()=>y(()=>import("./git-useful-commands.html-2e056050.js"),[]).then(({data:e})=>e),"v-4008ff77":()=>y(()=>import("./gitlab-ci.html-2b32bfd2.js"),[]).then(({data:e})=>e),"v-0fbf5fdc":()=>y(()=>import("./rethinking-git-flow.html-39a67167.js"),[]).then(({data:e})=>e),"v-071be141":()=>y(()=>import("./use-command-line-tool-to-manage-gitlab-merge-request.html-4c25576d.js"),[]).then(({data:e})=>e),"v-86a15cb6":()=>y(()=>import("./Resolving-Common-Problems-in-IntelliJ-IDEA.html-4d83e594.js"),[]).then(({data:e})=>e),"v-6dc18ec6":()=>y(()=>import("./Resolving-Common-Problems-in-Maven.md.html-4d4645f7.js"),[]).then(({data:e})=>e),"v-538a98d7":()=>y(()=>import("./avoid-sending-password-in-plaintext.html-6233e8a7.js"),[]).then(({data:e})=>e),"v-2f6ae09c":()=>y(()=>import("./check-if-name-exists.html-4b9b7d86.js"),[]).then(({data:e})=>e),"v-b5a78a7a":()=>y(()=>import("./common-practices-for-handling-excel.html-0efdabf9.js"),[]).then(({data:e})=>e),"v-100d1814":()=>y(()=>import("./how-to-convert-snapshot-into-release-jar-without-source-code.html-66363334.js"),[]).then(({data:e})=>e),"v-f5471cb0":()=>y(()=>import("./recommend-practices-for-query-by-date-range.html-9cb3e939.js"),[]).then(({data:e})=>e),"v-1aafac08":()=>y(()=>import("./why-i-prefer-fastjson-instead-of-jackson.html-f1819a15.js"),[]).then(({data:e})=>e),"v-ca672354":()=>y(()=>import("./why-is-it-so-hard-to-upgrade-dependencies.html-f4295ff8.js"),[]).then(({data:e})=>e),"v-143a8bce":()=>y(()=>import("./mysql-backup-case-study-mysqldump-in-action.html-cea2b606.js"),[]).then(({data:e})=>e),"v-7e2c7a0c":()=>y(()=>import("./mysql-data-migration-case-study-add-auto-increment.html-75552c95.js"),[]).then(({data:e})=>e),"v-f297935a":()=>y(()=>import("./mysql-details-you-should-know-when-execute-sql-in-command-line.html-9a9e576f.js"),[]).then(({data:e})=>e),"v-5b37b3c6":()=>y(()=>import("./export-mysql-table-into-excel.html-9bc68678.js"),[]).then(({data:e})=>e),"v-113531b4":()=>y(()=>import("./unit-testing-overview.html-9597f90a.js"),[]).then(({data:e})=>e),"v-65b23736":()=>y(()=>import("./use-RestAssured-for-api-testing.html-b8869f75.js"),[]).then(({data:e})=>e),"v-c488ac58":()=>y(()=>import("./use-cypress-for-e2e-testing.html-a3aed518.js"),[]).then(({data:e})=>e),"v-efcacba2":()=>y(()=>import("./use-jest-for-test-driven-development.html-842be257.js"),[]).then(({data:e})=>e),"v-0be1af08":()=>y(()=>import("./use-playwright-for-ui-testing.html-39747b36.js"),[]).then(({data:e})=>e),"v-75616b85":()=>y(()=>import("./use-postman-for-api-testing.html-69020f9d.js"),[]).then(({data:e})=>e),"v-17809471":()=>y(()=>import("./how-to-connect-to-internet.html-30b7db89.js"),[]).then(({data:e})=>e),"v-3706649a":()=>y(()=>import("./404.html-f2d5dd87.js"),[]).then(({data:e})=>e),"v-79c9f96f":()=>y(()=>import("./index.html-6a79c9ec.js"),[]).then(({data:e})=>e),"v-43539db8":()=>y(()=>import("./index.html-8779bfb9.js"),[]).then(({data:e})=>e),"v-06198984":()=>y(()=>import("./index.html-6039efb3.js"),[]).then(({data:e})=>e),"v-74473916":()=>y(()=>import("./index.html-b483266e.js"),[]).then(({data:e})=>e),"v-14c69af4":()=>y(()=>import("./index.html-08f7d006.js"),[]).then(({data:e})=>e),"v-eb072ff4":()=>y(()=>import("./index.html-bdb5e891.js"),[]).then(({data:e})=>e),"v-63cd5dba":()=>y(()=>import("./index.html-6866fdfe.js"),[]).then(({data:e})=>e),"v-0df55bac":()=>y(()=>import("./index.html-b3692dcf.js"),[]).then(({data:e})=>e),"v-d440f426":()=>y(()=>import("./index.html-aff33757.js"),[]).then(({data:e})=>e),"v-5bc93818":()=>y(()=>import("./index.html-fe376c9d.js"),[]).then(({data:e})=>e),"v-744d024e":()=>y(()=>import("./index.html-7b393b72.js"),[]).then(({data:e})=>e),"v-e52c881c":()=>y(()=>import("./index.html-1bb998c5.js"),[]).then(({data:e})=>e),"v-154dc4c4":()=>y(()=>import("./index.html-fa3db4ea.js"),[]).then(({data:e})=>e),"v-01560935":()=>y(()=>import("./index.html-c98c047d.js"),[]).then(({data:e})=>e),"v-3d5315f8":()=>y(()=>import("./index.html-e4ea5f08.js"),[]).then(({data:e})=>e),"v-1b3ae9cf":()=>y(()=>import("./index.html-db9a5c1a.js"),[]).then(({data:e})=>e),"v-007c0ae2":()=>y(()=>import("./index.html-8811bd65.js"),[]).then(({data:e})=>e),"v-211f44ee":()=>y(()=>import("./index.html-5fa1740f.js"),[]).then(({data:e})=>e),"v-6106c001":()=>y(()=>import("./index.html-4fc033c7.js"),[]).then(({data:e})=>e),"v-1bee38ca":()=>y(()=>import("./index.html-c50be8cb.js"),[]).then(({data:e})=>e),"v-0da0abf9":()=>y(()=>import("./index.html-90b5caba.js"),[]).then(({data:e})=>e),"v-5a3e80fc":()=>y(()=>import("./index.html-96e262ec.js"),[]).then(({data:e})=>e),"v-01c1de5b":()=>y(()=>import("./index.html-3d1d7f95.js"),[]).then(({data:e})=>e),"v-29350809":()=>y(()=>import("./index.html-fa48630e.js"),[]).then(({data:e})=>e),"v-50d6e023":()=>y(()=>import("./index.html-62782fea.js"),[]).then(({data:e})=>e),"v-0ca0efe6":()=>y(()=>import("./index.html-dc40d5ff.js"),[]).then(({data:e})=>e),"v-b310d42a":()=>y(()=>import("./index.html-5b2edaa1.js"),[]).then(({data:e})=>e),"v-13275df4":()=>y(()=>import("./index.html-15cad1de.js"),[]).then(({data:e})=>e),"v-28a1d8bf":()=>y(()=>import("./index.html-c22f0db4.js"),[]).then(({data:e})=>e),"v-60379330":()=>y(()=>import("./index.html-6d3cb5cc.js"),[]).then(({data:e})=>e),"v-245f5676":()=>y(()=>import("./index.html-44e80ad7.js"),[]).then(({data:e})=>e),"v-3b951558":()=>y(()=>import("./index.html-db761428.js"),[]).then(({data:e})=>e),"v-48b3e46d":()=>y(()=>import("./index.html-4736b52e.js"),[]).then(({data:e})=>e)},E0=JSON.parse(`{"base":"/","lang":"zh-CN","title":"levy","description":"levy's blog","head":[["meta",{"name":"google-site-verification","content":"XSoaUnV59ACn-fVEvYre2y_5mka_7o_wEoMPBQpwo2M"}],["script",{"async":true,"src":"https://www.googletagmanager.com/gtag/js?id=G-6HEW6B1S6B"}],["script",{},["window.dataLayer = window.dataLayer || [];\\nfunction gtag(){dataLayer.push(arguments);}\\ngtag('js', new Date());\\ngtag('config','G-6HEW6B1S6B');"]],["link",{"rel":"alternate","type":"application/rss+xml","href":"https://levy.vip/rss.xml","title":"levy RSS Feed"}]],"locales":{}}`);var x0=([e,t,l])=>e==="meta"&&t.name?`${e}.${t.name}`:["title","base"].includes(e)?e:e==="template"&&t.id?`${e}.${t.id}`:JSON.stringify([e,t,l]),L0=e=>{const t=new Set,l=[];return e.forEach(n=>{const a=x0(n);t.has(a)||(t.add(a),l.push(n))}),l},T0=e=>e[e.length-1]==="/"||e.endsWith(".html")?e:`${e}/`,A0=e=>e.startsWith("ftp://"),Zt=e=>/^(https?:)?\/\//.test(e),I0=/.md((\?|#).*)?$/,Bn=(e,t="/")=>!!(Zt(e)||A0(e)||e.startsWith("/")&&!e.startsWith(t)&&!I0.test(e)),Io=e=>/^mailto:/.test(e),P0=e=>/^tel:/.test(e),xi=e=>Object.prototype.toString.call(e)==="[object Object]",Li=e=>e[e.length-1]==="/"?e.slice(0,-1):e,Po=e=>e[0]==="/"?e.slice(1):e,O0=(e,t)=>{const l=Object.keys(e).sort((n,a)=>{const i=a.split("/").length-n.split("/").length;return i!==0?i:a.length-n.length});for(const n of l)if(t.startsWith(n))return n;return"/"};const Oo={"v-8daa1a0e":D(()=>y(()=>import("./index.html-3a97ce45.js"),["assets/index.html-3a97ce45.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-22a39d25":D(()=>y(()=>import("./about.html-a18bdc03.js"),["assets/about.html-a18bdc03.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-30a50b00":D(()=>y(()=>import("./a-vuepress2-entertaining-video.html-11220e81.js"),["assets/a-vuepress2-entertaining-video.html-11220e81.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0481cc80":D(()=>y(()=>import("./a-warning-from-navicat.html-1f409a81.js"),["assets/a-warning-from-navicat.html-1f409a81.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-31eac486":D(()=>y(()=>import("./about-arm-things-you-need-to-know.html-0feee41c.js"),["assets/about-arm-things-you-need-to-know.html-0feee41c.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-79e139f8":D(()=>y(()=>import("./beyond-utf8-do-you-know-utf8mb4-and-collation.html-85d76568.js"),["assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-85d76568.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-7a74360a":D(()=>y(()=>import("./claude-ai-in-action-extract-info-from-html.html-2568b3bd.js"),["assets/claude-ai-in-action-extract-info-from-html.html-2568b3bd.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-76f251d2":D(()=>y(()=>import("./copy-code-may-not-be-guilty.html-6eb3011e.js"),["assets/copy-code-may-not-be-guilty.html-6eb3011e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-f47f129e":D(()=>y(()=>import("./dont-try-to-argue-with-a-sb.html-6b8dcf17.js"),["assets/dont-try-to-argue-with-a-sb.html-6b8dcf17.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-fd7b7f92":D(()=>y(()=>import("./iteration-retrospective-of-sanyuan.html-27cf4da2.js"),["assets/iteration-retrospective-of-sanyuan.html-27cf4da2.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-81a71778":D(()=>y(()=>import("./leverage-ai-to-boost-coding-productivity.html-b3dab214.js"),["assets/leverage-ai-to-boost-coding-productivity.html-b3dab214.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5c48d497":D(()=>y(()=>import("./reflections-on-a-speech-by-cto-of-microsoft-china.html-32094be4.js"),["assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-32094be4.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1e305501":D(()=>y(()=>import("./things-I-have-to-vent-about-vue.html-862aa8df.js"),["assets/things-I-have-to-vent-about-vue.html-862aa8df.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-404740fa":D(()=>y(()=>import("./use-claude2-instead-of-chatgpt.html-cc6aebc1.js"),["assets/use-claude2-instead-of-chatgpt.html-cc6aebc1.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-f1efc11c":D(()=>y(()=>import("./vim-creator-pass-away.html-c6774dfb.js"),["assets/vim-creator-pass-away.html-c6774dfb.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-414fd2b2":D(()=>y(()=>import("./what-is-the-difference-between-sh-and-bash.html-41ae2768.js"),["assets/what-is-the-difference-between-sh-and-bash.html-41ae2768.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-bd2b5fe8":D(()=>y(()=>import("./you-dont-need-to-add-tenant_id-to-every-table.html-d1b8df99.js"),["assets/you-dont-need-to-add-tenant_id-to-every-table.html-d1b8df99.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-971cc7fe":D(()=>y(()=>import("./contemporary-college-english-1.html-8191f95a.js"),["assets/contemporary-college-english-1.html-8191f95a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-93b316c0":D(()=>y(()=>import("./contemporary-college-english-2.html-82c3bbbf.js"),["assets/contemporary-college-english-2.html-82c3bbbf.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-90496582":D(()=>y(()=>import("./contemporary-college-english-3.html-f1b95697.js"),["assets/contemporary-college-english-3.html-f1b95697.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-8cdfb444":D(()=>y(()=>import("./contemporary-college-english-4.html-bc05713f.js"),["assets/contemporary-college-english-4.html-bc05713f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-89760306":D(()=>y(()=>import("./contemporary-college-english-5.html-b75c916f.js"),["assets/contemporary-college-english-5.html-b75c916f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-860c51c8":D(()=>y(()=>import("./contemporary-college-english-6.html-cadc7c30.js"),["assets/contemporary-college-english-6.html-cadc7c30.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-246f4f17":D(()=>y(()=>import("./everyone-can-learn-english-1-overview.html-50a1c72e.js"),["assets/everyone-can-learn-english-1-overview.html-50a1c72e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3ac0474c":D(()=>y(()=>import("./everyone-can-learn-english-2-pronunciation.html-991c3fa5.js"),["assets/everyone-can-learn-english-2-pronunciation.html-991c3fa5.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-58a409d7":D(()=>y(()=>import("./everyone-can-learn-english-3-words.html-10909866.js"),["assets/everyone-can-learn-english-3-words.html-10909866.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-788d194a":D(()=>y(()=>import("./everyone-can-learn-english-4-listening-and-speaking.html-37f330d6.js"),["assets/everyone-can-learn-english-4-listening-and-speaking.html-37f330d6.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-ae153a4e":D(()=>y(()=>import("./everyone-can-learn-english-5-reading-and-writing.html-63730d51.js"),["assets/everyone-can-learn-english-5-reading-and-writing.html-63730d51.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-d42db13c":D(()=>y(()=>import("./how-to-self-evaluate-english-level.html-bc3d8f29.js"),["assets/how-to-self-evaluate-english-level.html-bc3d8f29.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-6ed7d996":D(()=>y(()=>import("./learning-7000-words-task-completed.html-55872900.js"),["assets/learning-7000-words-task-completed.html-55872900.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-221efd1f":D(()=>y(()=>import("./let-chatgpt-be-your-foreign-language-teacher.html-646de3af.js"),["assets/let-chatgpt-be-your-foreign-language-teacher.html-646de3af.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-72e84a92":D(()=>y(()=>import("./old-articles.html-3441ffdc.js"),["assets/old-articles.html-3441ffdc.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-7320140c":D(()=>y(()=>import("./performance-optimization-in-action.html-963023b4.js"),["assets/performance-optimization-in-action.html-963023b4.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1e5872c4":D(()=>y(()=>import("./git-best-pratices.html-73d0401b.js"),["assets/git-best-pratices.html-73d0401b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-48e494ef":D(()=>y(()=>import("./git-definitive-guide-to-merge-code.html-39271ae4.js"),["assets/git-definitive-guide-to-merge-code.html-39271ae4.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-60021cbc":D(()=>y(()=>import("./git-history-two-tricks-in-idea.html-3b5a7689.js"),["assets/git-history-two-tricks-in-idea.html-3b5a7689.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-642eaaea":D(()=>y(()=>import("./git-useful-commands.html-17a54d4f.js"),["assets/git-useful-commands.html-17a54d4f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-4008ff77":D(()=>y(()=>import("./gitlab-ci.html-e4938506.js"),["assets/gitlab-ci.html-e4938506.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0fbf5fdc":D(()=>y(()=>import("./rethinking-git-flow.html-2dbc1f93.js"),["assets/rethinking-git-flow.html-2dbc1f93.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-071be141":D(()=>y(()=>import("./use-command-line-tool-to-manage-gitlab-merge-request.html-bb0e24b0.js"),["assets/use-command-line-tool-to-manage-gitlab-merge-request.html-bb0e24b0.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-86a15cb6":D(()=>y(()=>import("./Resolving-Common-Problems-in-IntelliJ-IDEA.html-052f4669.js"),["assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-052f4669.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-6dc18ec6":D(()=>y(()=>import("./Resolving-Common-Problems-in-Maven.md.html-baa4321e.js"),["assets/Resolving-Common-Problems-in-Maven.md.html-baa4321e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-538a98d7":D(()=>y(()=>import("./avoid-sending-password-in-plaintext.html-43f53b8a.js"),["assets/avoid-sending-password-in-plaintext.html-43f53b8a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-2f6ae09c":D(()=>y(()=>import("./check-if-name-exists.html-abc8b8dd.js"),["assets/check-if-name-exists.html-abc8b8dd.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-b5a78a7a":D(()=>y(()=>import("./common-practices-for-handling-excel.html-82289612.js"),["assets/common-practices-for-handling-excel.html-82289612.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-100d1814":D(()=>y(()=>import("./how-to-convert-snapshot-into-release-jar-without-source-code.html-77c8640b.js"),["assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-77c8640b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-f5471cb0":D(()=>y(()=>import("./recommend-practices-for-query-by-date-range.html-c58f48b5.js"),["assets/recommend-practices-for-query-by-date-range.html-c58f48b5.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1aafac08":D(()=>y(()=>import("./why-i-prefer-fastjson-instead-of-jackson.html-3961785e.js"),["assets/why-i-prefer-fastjson-instead-of-jackson.html-3961785e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-ca672354":D(()=>y(()=>import("./why-is-it-so-hard-to-upgrade-dependencies.html-08962984.js"),["assets/why-is-it-so-hard-to-upgrade-dependencies.html-08962984.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-143a8bce":D(()=>y(()=>import("./mysql-backup-case-study-mysqldump-in-action.html-27652577.js"),["assets/mysql-backup-case-study-mysqldump-in-action.html-27652577.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-7e2c7a0c":D(()=>y(()=>import("./mysql-data-migration-case-study-add-auto-increment.html-0e5bf45b.js"),["assets/mysql-data-migration-case-study-add-auto-increment.html-0e5bf45b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-f297935a":D(()=>y(()=>import("./mysql-details-you-should-know-when-execute-sql-in-command-line.html-834b3dd8.js"),["assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-834b3dd8.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5b37b3c6":D(()=>y(()=>import("./export-mysql-table-into-excel.html-6db61b30.js"),["assets/export-mysql-table-into-excel.html-6db61b30.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-113531b4":D(()=>y(()=>import("./unit-testing-overview.html-aab524e2.js"),["assets/unit-testing-overview.html-aab524e2.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-65b23736":D(()=>y(()=>import("./use-RestAssured-for-api-testing.html-badc2513.js"),["assets/use-RestAssured-for-api-testing.html-badc2513.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-c488ac58":D(()=>y(()=>import("./use-cypress-for-e2e-testing.html-1c183bce.js"),["assets/use-cypress-for-e2e-testing.html-1c183bce.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-efcacba2":D(()=>y(()=>import("./use-jest-for-test-driven-development.html-8fbc9f0a.js"),["assets/use-jest-for-test-driven-development.html-8fbc9f0a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0be1af08":D(()=>y(()=>import("./use-playwright-for-ui-testing.html-44356ace.js"),["assets/use-playwright-for-ui-testing.html-44356ace.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-75616b85":D(()=>y(()=>import("./use-postman-for-api-testing.html-557f4cb8.js"),["assets/use-postman-for-api-testing.html-557f4cb8.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-17809471":D(()=>y(()=>import("./how-to-connect-to-internet.html-994bb0e3.js"),["assets/how-to-connect-to-internet.html-994bb0e3.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3706649a":D(()=>y(()=>import("./404.html-fc95297b.js"),["assets/404.html-fc95297b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-79c9f96f":D(()=>y(()=>import("./index.html-a0b725d1.js"),["assets/index.html-a0b725d1.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-43539db8":D(()=>y(()=>import("./index.html-1d3ab01e.js"),["assets/index.html-1d3ab01e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-06198984":D(()=>y(()=>import("./index.html-b69b0952.js"),["assets/index.html-b69b0952.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-74473916":D(()=>y(()=>import("./index.html-6d6c9ccc.js"),["assets/index.html-6d6c9ccc.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-14c69af4":D(()=>y(()=>import("./index.html-e013b2c1.js"),["assets/index.html-e013b2c1.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-eb072ff4":D(()=>y(()=>import("./index.html-f1f01e82.js"),["assets/index.html-f1f01e82.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-63cd5dba":D(()=>y(()=>import("./index.html-5a646c34.js"),["assets/index.html-5a646c34.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0df55bac":D(()=>y(()=>import("./index.html-b9ef174d.js"),["assets/index.html-b9ef174d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-d440f426":D(()=>y(()=>import("./index.html-86d779f8.js"),["assets/index.html-86d779f8.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5bc93818":D(()=>y(()=>import("./index.html-956353ed.js"),["assets/index.html-956353ed.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-744d024e":D(()=>y(()=>import("./index.html-feab3d86.js"),["assets/index.html-feab3d86.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-e52c881c":D(()=>y(()=>import("./index.html-2728cdd7.js"),["assets/index.html-2728cdd7.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-154dc4c4":D(()=>y(()=>import("./index.html-9890dac2.js"),["assets/index.html-9890dac2.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-01560935":D(()=>y(()=>import("./index.html-967c662b.js"),["assets/index.html-967c662b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3d5315f8":D(()=>y(()=>import("./index.html-e9e56fc9.js"),["assets/index.html-e9e56fc9.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1b3ae9cf":D(()=>y(()=>import("./index.html-049bd54f.js"),["assets/index.html-049bd54f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-007c0ae2":D(()=>y(()=>import("./index.html-06bf462d.js"),["assets/index.html-06bf462d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-211f44ee":D(()=>y(()=>import("./index.html-b5310c3c.js"),["assets/index.html-b5310c3c.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-6106c001":D(()=>y(()=>import("./index.html-bdcc3d36.js"),["assets/index.html-bdcc3d36.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1bee38ca":D(()=>y(()=>import("./index.html-18a625f7.js"),["assets/index.html-18a625f7.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0da0abf9":D(()=>y(()=>import("./index.html-9122fca1.js"),["assets/index.html-9122fca1.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5a3e80fc":D(()=>y(()=>import("./index.html-1d219a24.js"),["assets/index.html-1d219a24.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-01c1de5b":D(()=>y(()=>import("./index.html-342e7095.js"),["assets/index.html-342e7095.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-29350809":D(()=>y(()=>import("./index.html-f4939e44.js"),["assets/index.html-f4939e44.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-50d6e023":D(()=>y(()=>import("./index.html-6635a236.js"),["assets/index.html-6635a236.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0ca0efe6":D(()=>y(()=>import("./index.html-55bb043c.js"),["assets/index.html-55bb043c.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-b310d42a":D(()=>y(()=>import("./index.html-9070516e.js"),["assets/index.html-9070516e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-13275df4":D(()=>y(()=>import("./index.html-0f5c0f0d.js"),["assets/index.html-0f5c0f0d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-28a1d8bf":D(()=>y(()=>import("./index.html-5859e69f.js"),["assets/index.html-5859e69f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-60379330":D(()=>y(()=>import("./index.html-a6b0d770.js"),["assets/index.html-a6b0d770.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-245f5676":D(()=>y(()=>import("./index.html-8be4c9cb.js"),["assets/index.html-8be4c9cb.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3b951558":D(()=>y(()=>import("./index.html-05a6aceb.js"),["assets/index.html-05a6aceb.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-48b3e46d":D(()=>y(()=>import("./index.html-b4618361.js"),["assets/index.html-b4618361.js","assets/plugin-vue_export-helper-c27b6911.js"]))};var C0=Symbol(""),R0=W(w0),Co=Yt({key:"",path:"",title:"",lang:"",frontmatter:{},headers:[]}),Dt=W(Co),ue=()=>Dt,Ro=Symbol(""),Ee=()=>{const e=me(Ro);if(!e)throw new Error("usePageFrontmatter() is called without provider.");return e},So=Symbol(""),S0=()=>{const e=me(So);if(!e)throw new Error("usePageHead() is called without provider.");return e},D0=Symbol(""),Do=Symbol(""),$o=()=>{const e=me(Do);if(!e)throw new Error("usePageLang() is called without provider.");return e},Mo=Symbol(""),$0=()=>{const e=me(Mo);if(!e)throw new Error("usePageLayout() is called without provider.");return e},Ti=Symbol(""),mt=()=>{const e=me(Ti);if(!e)throw new Error("useRouteLocale() is called without provider.");return e},ol=W(E0),Vo=()=>ol,Fo=Symbol(""),rn=()=>{const e=me(Fo);if(!e)throw new Error("useSiteLocaleData() is called without provider.");return e},M0=Symbol(""),V0="Layout",F0="NotFound",wt=tn({resolveLayouts:e=>e.reduce((t,l)=>({...t,...l.layouts}),{}),resolvePageData:async e=>{const t=R0.value[e];return await(t==null?void 0:t())??Co},resolvePageFrontmatter:e=>e.frontmatter,resolvePageHead:(e,t,l)=>{const n=ae(t.description)?t.description:l.description,a=[...X(t.head)?t.head:[],...l.head,["title",{},e],["meta",{name:"description",content:n}]];return L0(a)},resolvePageHeadTitle:(e,t)=>[e.title,t.title].filter(l=>!!l).join(" | "),resolvePageLang:(e,t)=>e.lang||t.lang||"en-US",resolvePageLayout:(e,t)=>{let l;if(e.path){const n=e.frontmatter.layout;ae(n)?l=n:l=V0}else l=F0;return t[l]},resolveRouteLocale:(e,t)=>O0(e,t),resolveSiteLocaleData:(e,t)=>({...e,...e.locales[t]})}),Zn=M({name:"ClientOnly",setup(e,t){const l=W(!1);return ke(()=>{l.value=!0}),()=>{var n,a;return l.value?(a=(n=t.slots).default)==null?void 0:a.call(n):null}}}),No=M({name:"Content",props:{pageKey:{type:String,required:!1,default:""}},setup(e){const t=ue(),l=E(()=>Oo[e.pageKey||t.value.key]);return()=>l.value?s(l.value):s("div","404 Not Found")}}),dt=(e={})=>e,Le=e=>Zt(e)?e:`/${Po(e)}`;const N0={};/*! - * vue-router v4.2.4 - * (c) 2023 Eduardo San Martin Morote - * @license MIT - */const rl=typeof window<"u";function j0(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const ge=Object.assign;function ba(e,t){const l={};for(const n in t){const a=t[n];l[n]=ut(a)?a.map(e):e(a)}return l}const jl=()=>{},ut=Array.isArray,B0=/\/$/,z0=e=>e.replace(B0,"");function _a(e,t,l="/"){let n,a={},i="",r="";const o=t.indexOf("#");let c=t.indexOf("?");return o=0&&(c=-1),c>-1&&(n=t.slice(0,c),i=t.slice(c+1,o>-1?o:t.length),a=e(i)),o>-1&&(n=n||t.slice(0,o),r=t.slice(o,t.length)),n=G0(n??t,l),{fullPath:n+(i&&"?")+i+r,path:n,query:a,hash:r}}function H0(e,t){const l=t.query?e(t.query):"";return t.path+(l&&"?")+l+(t.hash||"")}function $r(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function q0(e,t,l){const n=t.matched.length-1,a=l.matched.length-1;return n>-1&&n===a&&yl(t.matched[n],l.matched[a])&&jo(t.params,l.params)&&e(t.query)===e(l.query)&&t.hash===l.hash}function yl(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function jo(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const l in e)if(!U0(e[l],t[l]))return!1;return!0}function U0(e,t){return ut(e)?Mr(e,t):ut(t)?Mr(t,e):e===t}function Mr(e,t){return ut(t)?e.length===t.length&&e.every((l,n)=>l===t[n]):e.length===1&&e[0]===t}function G0(e,t){if(e.startsWith("/"))return e;if(!e)return t;const l=t.split("/"),n=e.split("/"),a=n[n.length-1];(a===".."||a===".")&&n.push("");let i=l.length-1,r,o;for(r=0;r1&&i--;else break;return l.slice(0,i).join("/")+"/"+n.slice(r-(r===n.length?1:0)).join("/")}var Jl;(function(e){e.pop="pop",e.push="push"})(Jl||(Jl={}));var Bl;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Bl||(Bl={}));function W0(e){if(!e)if(rl){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),z0(e)}const K0=/^[^#]+#/;function J0(e,t){return e.replace(K0,"#")+t}function Q0(e,t){const l=document.documentElement.getBoundingClientRect(),n=e.getBoundingClientRect();return{behavior:t.behavior,left:n.left-l.left-(t.left||0),top:n.top-l.top-(t.top||0)}}const ea=()=>({left:window.pageXOffset,top:window.pageYOffset});function Y0(e){let t;if("el"in e){const l=e.el,n=typeof l=="string"&&l.startsWith("#"),a=typeof l=="string"?n?document.getElementById(l.slice(1)):document.querySelector(l):l;if(!a)return;t=Q0(a,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.pageXOffset,t.top!=null?t.top:window.pageYOffset)}function Vr(e,t){return(history.state?history.state.position-t:-1)+e}const Ba=new Map;function X0(e,t){Ba.set(e,t)}function Z0(e){const t=Ba.get(e);return Ba.delete(e),t}let e1=()=>location.protocol+"//"+location.host;function Bo(e,t){const{pathname:l,search:n,hash:a}=t,i=e.indexOf("#");if(i>-1){let o=a.includes(e.slice(i))?e.slice(i).length:1,c=a.slice(o);return c[0]!=="/"&&(c="/"+c),$r(c,"")}return $r(l,e)+n+a}function t1(e,t,l,n){let a=[],i=[],r=null;const o=({state:h})=>{const f=Bo(e,location),b=l.value,w=t.value;let x=0;if(h){if(l.value=f,t.value=h,r&&r===b){r=null;return}x=w?h.position-w.position:0}else n(f);a.forEach(g=>{g(l.value,b,{delta:x,type:Jl.pop,direction:x?x>0?Bl.forward:Bl.back:Bl.unknown})})};function c(){r=l.value}function u(h){a.push(h);const f=()=>{const b=a.indexOf(h);b>-1&&a.splice(b,1)};return i.push(f),f}function d(){const{history:h}=window;h.state&&h.replaceState(ge({},h.state,{scroll:ea()}),"")}function p(){for(const h of i)h();i=[],window.removeEventListener("popstate",o),window.removeEventListener("beforeunload",d)}return window.addEventListener("popstate",o),window.addEventListener("beforeunload",d,{passive:!0}),{pauseListeners:c,listen:u,destroy:p}}function Fr(e,t,l,n=!1,a=!1){return{back:e,current:t,forward:l,replaced:n,position:window.history.length,scroll:a?ea():null}}function l1(e){const{history:t,location:l}=window,n={value:Bo(e,l)},a={value:t.state};a.value||i(n.value,{back:null,current:n.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function i(c,u,d){const p=e.indexOf("#"),h=p>-1?(l.host&&document.querySelector("base")?e:e.slice(p))+c:e1()+e+c;try{t[d?"replaceState":"pushState"](u,"",h),a.value=u}catch(f){console.error(f),l[d?"replace":"assign"](h)}}function r(c,u){const d=ge({},t.state,Fr(a.value.back,c,a.value.forward,!0),u,{position:a.value.position});i(c,d,!0),n.value=c}function o(c,u){const d=ge({},a.value,t.state,{forward:c,scroll:ea()});i(d.current,d,!0);const p=ge({},Fr(n.value,c,null),{position:d.position+1},u);i(c,p,!1),n.value=c}return{location:n,state:a,push:o,replace:r}}function n1(e){e=W0(e);const t=l1(e),l=t1(e,t.state,t.location,t.replace);function n(i,r=!0){r||l.pauseListeners(),history.go(i)}const a=ge({location:"",base:e,go:n,createHref:J0.bind(null,e)},t,l);return Object.defineProperty(a,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(a,"state",{enumerable:!0,get:()=>t.state.value}),a}function a1(e){return typeof e=="string"||e&&typeof e=="object"}function zo(e){return typeof e=="string"||typeof e=="symbol"}const Et={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},Ho=Symbol("");var Nr;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(Nr||(Nr={}));function bl(e,t){return ge(new Error,{type:e,[Ho]:!0},t)}function _t(e,t){return e instanceof Error&&Ho in e&&(t==null||!!(e.type&t))}const jr="[^/]+?",i1={sensitive:!1,strict:!1,start:!0,end:!0},r1=/[.+*?^${}()[\]/\\]/g;function s1(e,t){const l=ge({},i1,t),n=[];let a=l.start?"^":"";const i=[];for(const u of e){const d=u.length?[]:[90];l.strict&&!u.length&&(a+="/");for(let p=0;pt.length?t.length===1&&t[0]===40+40?1:-1:0}function c1(e,t){let l=0;const n=e.score,a=t.score;for(;l0&&t[t.length-1]<0}const u1={type:0,value:""},d1=/[a-zA-Z0-9_]/;function p1(e){if(!e)return[[]];if(e==="/")return[[u1]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(f){throw new Error(`ERR (${l})/"${u}": ${f}`)}let l=0,n=l;const a=[];let i;function r(){i&&a.push(i),i=[]}let o=0,c,u="",d="";function p(){u&&(l===0?i.push({type:0,value:u}):l===1||l===2||l===3?(i.length>1&&(c==="*"||c==="+")&&t(`A repeatable param (${u}) must be alone in its segment. eg: '/:ids+.`),i.push({type:1,value:u,regexp:d,repeatable:c==="*"||c==="+",optional:c==="*"||c==="?"})):t("Invalid state to consume buffer"),u="")}function h(){u+=c}for(;o{r(_)}:jl}function r(d){if(zo(d)){const p=n.get(d);p&&(n.delete(d),l.splice(l.indexOf(p),1),p.children.forEach(r),p.alias.forEach(r))}else{const p=l.indexOf(d);p>-1&&(l.splice(p,1),d.record.name&&n.delete(d.record.name),d.children.forEach(r),d.alias.forEach(r))}}function o(){return l}function c(d){let p=0;for(;p=0&&(d.record.path!==l[p].record.path||!qo(d,l[p]));)p++;l.splice(p,0,d),d.record.name&&!Hr(d)&&n.set(d.record.name,d)}function u(d,p){let h,f={},b,w;if("name"in d&&d.name){if(h=n.get(d.name),!h)throw bl(1,{location:d});w=h.record.name,f=ge(zr(p.params,h.keys.filter(_=>!_.optional).map(_=>_.name)),d.params&&zr(d.params,h.keys.map(_=>_.name))),b=h.stringify(f)}else if("path"in d)b=d.path,h=l.find(_=>_.re.test(b)),h&&(f=h.parse(b),w=h.record.name);else{if(h=p.name?n.get(p.name):l.find(_=>_.re.test(p.path)),!h)throw bl(1,{location:d,currentLocation:p});w=h.record.name,f=ge({},p.params,d.params),b=h.stringify(f)}const x=[];let g=h;for(;g;)x.unshift(g.record),g=g.parent;return{name:w,path:b,params:f,matched:x,meta:m1(x)}}return e.forEach(d=>i(d)),{addRoute:i,resolve:u,removeRoute:r,getRoutes:o,getRecordMatcher:a}}function zr(e,t){const l={};for(const n of t)n in e&&(l[n]=e[n]);return l}function v1(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:g1(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function g1(e){const t={},l=e.props||!1;if("component"in e)t.default=l;else for(const n in e.components)t[n]=typeof l=="object"?l[n]:l;return t}function Hr(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function m1(e){return e.reduce((t,l)=>ge(t,l.meta),{})}function qr(e,t){const l={};for(const n in e)l[n]=n in t?t[n]:e[n];return l}function qo(e,t){return t.children.some(l=>l===e||qo(e,l))}const Uo=/#/g,y1=/&/g,b1=/\//g,_1=/=/g,k1=/\?/g,Go=/\+/g,w1=/%5B/g,E1=/%5D/g,Wo=/%5E/g,x1=/%60/g,Ko=/%7B/g,L1=/%7C/g,Jo=/%7D/g,T1=/%20/g;function Ai(e){return encodeURI(""+e).replace(L1,"|").replace(w1,"[").replace(E1,"]")}function A1(e){return Ai(e).replace(Ko,"{").replace(Jo,"}").replace(Wo,"^")}function za(e){return Ai(e).replace(Go,"%2B").replace(T1,"+").replace(Uo,"%23").replace(y1,"%26").replace(x1,"`").replace(Ko,"{").replace(Jo,"}").replace(Wo,"^")}function I1(e){return za(e).replace(_1,"%3D")}function P1(e){return Ai(e).replace(Uo,"%23").replace(k1,"%3F")}function O1(e){return e==null?"":P1(e).replace(b1,"%2F")}function zn(e){try{return decodeURIComponent(""+e)}catch{}return""+e}function C1(e){const t={};if(e===""||e==="?")return t;const n=(e[0]==="?"?e.slice(1):e).split("&");for(let a=0;ai&&za(i)):[n&&za(n)]).forEach(i=>{i!==void 0&&(t+=(t.length?"&":"")+l,i!=null&&(t+="="+i))})}return t}function R1(e){const t={};for(const l in e){const n=e[l];n!==void 0&&(t[l]=ut(n)?n.map(a=>a==null?null:""+a):n==null?n:""+n)}return t}const S1=Symbol(""),Gr=Symbol(""),ta=Symbol(""),Ii=Symbol(""),Ha=Symbol("");function Cl(){let e=[];function t(n){return e.push(n),()=>{const a=e.indexOf(n);a>-1&&e.splice(a,1)}}function l(){e=[]}return{add:t,list:()=>e.slice(),reset:l}}function $t(e,t,l,n,a){const i=n&&(n.enterCallbacks[a]=n.enterCallbacks[a]||[]);return()=>new Promise((r,o)=>{const c=p=>{p===!1?o(bl(4,{from:l,to:t})):p instanceof Error?o(p):a1(p)?o(bl(2,{from:t,to:p})):(i&&n.enterCallbacks[a]===i&&typeof p=="function"&&i.push(p),r())},u=e.call(n&&n.instances[a],t,l,c);let d=Promise.resolve(u);e.length<3&&(d=d.then(c)),d.catch(p=>o(p))})}function ka(e,t,l,n){const a=[];for(const i of e)for(const r in i.components){let o=i.components[r];if(!(t!=="beforeRouteEnter"&&!i.instances[r]))if(D1(o)){const u=(o.__vccOpts||o)[t];u&&a.push($t(u,l,n,i,r))}else{let c=o();a.push(()=>c.then(u=>{if(!u)return Promise.reject(new Error(`Couldn't resolve component "${r}" at "${i.path}"`));const d=j0(u)?u.default:u;i.components[r]=d;const h=(d.__vccOpts||d)[t];return h&&$t(h,l,n,i,r)()}))}}return a}function D1(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function qa(e){const t=me(ta),l=me(Ii),n=E(()=>t.resolve(it(e.to))),a=E(()=>{const{matched:c}=n.value,{length:u}=c,d=c[u-1],p=l.matched;if(!d||!p.length)return-1;const h=p.findIndex(yl.bind(null,d));if(h>-1)return h;const f=Wr(c[u-2]);return u>1&&Wr(d)===f&&p[p.length-1].path!==f?p.findIndex(yl.bind(null,c[u-2])):h}),i=E(()=>a.value>-1&&F1(l.params,n.value.params)),r=E(()=>a.value>-1&&a.value===l.matched.length-1&&jo(l.params,n.value.params));function o(c={}){return V1(c)?t[it(e.replace)?"replace":"push"](it(e.to)).catch(jl):Promise.resolve()}return{route:n,href:E(()=>n.value.href),isActive:i,isExactActive:r,navigate:o}}const $1=M({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:qa,setup(e,{slots:t}){const l=tn(qa(e)),{options:n}=me(ta),a=E(()=>({[Kr(e.activeClass,n.linkActiveClass,"router-link-active")]:l.isActive,[Kr(e.exactActiveClass,n.linkExactActiveClass,"router-link-exact-active")]:l.isExactActive}));return()=>{const i=t.default&&t.default(l);return e.custom?i:s("a",{"aria-current":l.isExactActive?e.ariaCurrentValue:null,href:l.href,onClick:l.navigate,class:a.value},i)}}}),M1=$1;function V1(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function F1(e,t){for(const l in t){const n=t[l],a=e[l];if(typeof n=="string"){if(n!==a)return!1}else if(!ut(a)||a.length!==n.length||n.some((i,r)=>i!==a[r]))return!1}return!0}function Wr(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const Kr=(e,t,l)=>e??t??l,N1=M({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:l}){const n=me(Ha),a=E(()=>e.route||n.value),i=me(Gr,0),r=E(()=>{let u=it(i);const{matched:d}=a.value;let p;for(;(p=d[u])&&!p.components;)u++;return u}),o=E(()=>a.value.matched[r.value]);ot(Gr,E(()=>r.value+1)),ot(S1,o),ot(Ha,a);const c=W();return ce(()=>[c.value,o.value,e.name],([u,d,p],[h,f,b])=>{d&&(d.instances[p]=u,f&&f!==d&&u&&u===h&&(d.leaveGuards.size||(d.leaveGuards=f.leaveGuards),d.updateGuards.size||(d.updateGuards=f.updateGuards))),u&&d&&(!f||!yl(d,f)||!h)&&(d.enterCallbacks[p]||[]).forEach(w=>w(u))},{flush:"post"}),()=>{const u=a.value,d=e.name,p=o.value,h=p&&p.components[d];if(!h)return Jr(l.default,{Component:h,route:u});const f=p.props[d],b=f?f===!0?u.params:typeof f=="function"?f(u):f:null,x=s(h,ge({},b,t,{onVnodeUnmounted:g=>{g.component.isUnmounted&&(p.instances[d]=null)},ref:c}));return Jr(l.default,{Component:x,route:u})||x}}});function Jr(e,t){if(!e)return null;const l=e(t);return l.length===1?l[0]:l}const Qo=N1;function j1(e){const t=f1(e.routes,e),l=e.parseQuery||C1,n=e.stringifyQuery||Ur,a=e.history,i=Cl(),r=Cl(),o=Cl(),c=Ue(Et);let u=Et;rl&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const d=ba.bind(null,A=>""+A),p=ba.bind(null,O1),h=ba.bind(null,zn);function f(A,z){let N,K;return zo(A)?(N=t.getRecordMatcher(A),K=z):K=A,t.addRoute(K,N)}function b(A){const z=t.getRecordMatcher(A);z&&t.removeRoute(z)}function w(){return t.getRoutes().map(A=>A.record)}function x(A){return!!t.getRecordMatcher(A)}function g(A,z){if(z=ge({},z||c.value),typeof A=="string"){const k=_a(l,A,z.path),L=t.resolve({path:k.path},z),I=a.createHref(k.fullPath);return ge(k,L,{params:h(L.params),hash:zn(k.hash),redirectedFrom:void 0,href:I})}let N;if("path"in A)N=ge({},A,{path:_a(l,A.path,z.path).path});else{const k=ge({},A.params);for(const L in k)k[L]==null&&delete k[L];N=ge({},A,{params:p(k)}),z.params=p(z.params)}const K=t.resolve(N,z),ve=A.hash||"";K.params=d(h(K.params));const v=H0(n,ge({},A,{hash:A1(ve),path:K.path})),m=a.createHref(v);return ge({fullPath:v,hash:ve,query:n===Ur?R1(A.query):A.query||{}},K,{redirectedFrom:void 0,href:m})}function _(A){return typeof A=="string"?_a(l,A,c.value.path):ge({},A)}function P(A,z){if(u!==A)return bl(8,{from:z,to:A})}function O(A){return V(A)}function B(A){return O(ge(_(A),{replace:!0}))}function C(A){const z=A.matched[A.matched.length-1];if(z&&z.redirect){const{redirect:N}=z;let K=typeof N=="function"?N(A):N;return typeof K=="string"&&(K=K.includes("?")||K.includes("#")?K=_(K):{path:K},K.params={}),ge({query:A.query,hash:A.hash,params:"path"in K?{}:A.params},K)}}function V(A,z){const N=u=g(A),K=c.value,ve=A.state,v=A.force,m=A.replace===!0,k=C(N);if(k)return V(ge(_(k),{state:typeof k=="object"?ge({},ve,k.state):ve,force:v,replace:m}),z||N);const L=N;L.redirectedFrom=z;let I;return!v&&q0(n,K,N)&&(I=bl(16,{to:L,from:K}),pt(K,K,!0,!1)),(I?Promise.resolve(I):Y(L,K)).catch(R=>_t(R)?_t(R,2)?R:It(R):fe(R,L,K)).then(R=>{if(R){if(_t(R,2))return V(ge({replace:m},_(R.to),{state:typeof R.to=="object"?ge({},ve,R.to.state):ve,force:v}),z||L)}else R=H(L,K,!0,m,ve);return ne(L,K,R),R})}function T(A,z){const N=P(A,z);return N?Promise.reject(N):Promise.resolve()}function q(A){const z=ll.values().next().value;return z&&typeof z.runWithContext=="function"?z.runWithContext(A):A()}function Y(A,z){let N;const[K,ve,v]=B1(A,z);N=ka(K.reverse(),"beforeRouteLeave",A,z);for(const k of K)k.leaveGuards.forEach(L=>{N.push($t(L,A,z))});const m=T.bind(null,A,z);return N.push(m),De(N).then(()=>{N=[];for(const k of i.list())N.push($t(k,A,z));return N.push(m),De(N)}).then(()=>{N=ka(ve,"beforeRouteUpdate",A,z);for(const k of ve)k.updateGuards.forEach(L=>{N.push($t(L,A,z))});return N.push(m),De(N)}).then(()=>{N=[];for(const k of v)if(k.beforeEnter)if(ut(k.beforeEnter))for(const L of k.beforeEnter)N.push($t(L,A,z));else N.push($t(k.beforeEnter,A,z));return N.push(m),De(N)}).then(()=>(A.matched.forEach(k=>k.enterCallbacks={}),N=ka(v,"beforeRouteEnter",A,z),N.push(m),De(N))).then(()=>{N=[];for(const k of r.list())N.push($t(k,A,z));return N.push(m),De(N)}).catch(k=>_t(k,8)?k:Promise.reject(k))}function ne(A,z,N){o.list().forEach(K=>q(()=>K(A,z,N)))}function H(A,z,N,K,ve){const v=P(A,z);if(v)return v;const m=z===Et,k=rl?history.state:{};N&&(K||m?a.replace(A.fullPath,ge({scroll:m&&k&&k.scroll},ve)):a.push(A.fullPath,ve)),c.value=A,pt(A,z,N,m),It()}let ee;function G(){ee||(ee=a.listen((A,z,N)=>{if(!mn.listening)return;const K=g(A),ve=C(K);if(ve){V(ge(ve,{replace:!0}),K).catch(jl);return}u=K;const v=c.value;rl&&X0(Vr(v.fullPath,N.delta),ea()),Y(K,v).catch(m=>_t(m,12)?m:_t(m,2)?(V(m.to,K).then(k=>{_t(k,20)&&!N.delta&&N.type===Jl.pop&&a.go(-1,!1)}).catch(jl),Promise.reject()):(N.delta&&a.go(-N.delta,!1),fe(m,K,v))).then(m=>{m=m||H(K,v,!1),m&&(N.delta&&!_t(m,8)?a.go(-N.delta,!1):N.type===Jl.pop&&_t(m,20)&&a.go(-1,!1)),ne(K,v,m)}).catch(jl)}))}let Se=Cl(),he=Cl(),_e;function fe(A,z,N){It(A);const K=he.list();return K.length?K.forEach(ve=>ve(A,z,N)):console.error(A),Promise.reject(A)}function bt(){return _e&&c.value!==Et?Promise.resolve():new Promise((A,z)=>{Se.add([A,z])})}function It(A){return _e||(_e=!A,G(),Se.list().forEach(([z,N])=>A?N(A):z()),Se.reset()),A}function pt(A,z,N,K){const{scrollBehavior:ve}=e;if(!rl||!ve)return Promise.resolve();const v=!N&&Z0(Vr(A.fullPath,0))||(K||!N)&&history.state&&history.state.scroll||null;return Tl().then(()=>ve(A,z,v)).then(m=>m&&Y0(m)).catch(m=>fe(m,A,z))}const je=A=>a.go(A);let tl;const ll=new Set,mn={currentRoute:c,listening:!0,addRoute:f,removeRoute:b,hasRoute:x,getRoutes:w,resolve:g,options:e,push:O,replace:B,go:je,back:()=>je(-1),forward:()=>je(1),beforeEach:i.add,beforeResolve:r.add,afterEach:o.add,onError:he.add,isReady:bt,install(A){const z=this;A.component("RouterLink",M1),A.component("RouterView",Qo),A.config.globalProperties.$router=z,Object.defineProperty(A.config.globalProperties,"$route",{enumerable:!0,get:()=>it(c)}),rl&&!tl&&c.value===Et&&(tl=!0,O(a.location).catch(ve=>{}));const N={};for(const ve in Et)Object.defineProperty(N,ve,{get:()=>c.value[ve],enumerable:!0});A.provide(ta,z),A.provide(Ii,js(N)),A.provide(Ha,c);const K=A.unmount;ll.add(A),A.unmount=function(){ll.delete(A),ll.size<1&&(u=Et,ee&&ee(),ee=null,c.value=Et,tl=!1,_e=!1),K()}}};function De(A){return A.reduce((z,N)=>z.then(()=>q(N)),Promise.resolve())}return mn}function B1(e,t){const l=[],n=[],a=[],i=Math.max(t.matched.length,e.matched.length);for(let r=0;ryl(u,o))?n.push(o):l.push(o));const c=e.matched[r];c&&(t.matched.find(u=>yl(u,c))||a.push(c))}return[l,n,a]}function Ve(){return me(ta)}function yt(){return me(Ii)}const de=({name:e="",color:t="currentColor"},{slots:l})=>{var n;return s("svg",{xmlns:"http://www.w3.org/2000/svg",class:["icon",`${e}-icon`],viewBox:"0 0 1024 1024",fill:t,"aria-label":`${e} icon`},(n=l.default)==null?void 0:n.call(l))};de.displayName="IconBase";const Yo=({size:e=48,stroke:t=4,wrapper:l=!0,height:n=2*e})=>{const a=s("svg",{xmlns:"http://www.w3.org/2000/svg",width:e,height:e,preserveAspectRatio:"xMidYMid",viewBox:"25 25 50 50"},[s("animateTransform",{attributeName:"transform",type:"rotate",dur:"2s",keyTimes:"0;1",repeatCount:"indefinite",values:"0;360"}),s("circle",{cx:"50",cy:"50",r:"20",fill:"none",stroke:"currentColor","stroke-width":t,"stroke-linecap":"round"},[s("animate",{attributeName:"stroke-dasharray",dur:"1.5s",keyTimes:"0;0.5;1",repeatCount:"indefinite",values:"1,200;90,200;1,200"}),s("animate",{attributeName:"stroke-dashoffset",dur:"1.5s",keyTimes:"0;0.5;1",repeatCount:"indefinite",values:"0;-35px;-125px"})])]);return l?s("div",{class:"loading-icon-wrapper",style:`display:flex;align-items:center;justify-content:center;height:${n}px`},a):a};Yo.displayName="LoadingIcon";const Xo=(e,{slots:t})=>{var l;return(l=t.default)==null?void 0:l.call(t)},Pi=(e="")=>{if(e){if(typeof e=="number")return new Date(e);const t=Date.parse(e.toString());if(!Number.isNaN(t))return new Date(t)}return null},la=(e,t)=>{let l=1;for(let n=0;n>6;return l+=l<<3,l^=l>>11,l%t},Zo=Array.isArray,z1=e=>typeof e=="function",H1=e=>typeof e=="string";var q1=e=>e.startsWith("ftp://"),Oi=e=>/^(https?:)?\/\//.test(e),U1=/.md((\?|#).*)?$/,G1=(e,t="/")=>!!(Oi(e)||q1(e)||e.startsWith("/")&&!e.startsWith(t)&&!U1.test(e)),ec=e=>Object.prototype.toString.call(e)==="[object Object]";function W1(){const e=W(!1);return Xt()&&ke(()=>{e.value=!0}),e}function K1(e){return W1(),E(()=>!!e())}const Tt=e=>typeof e=="string",Ql=(e,t)=>Tt(e)&&e.startsWith(t),il=(e,t)=>Tt(e)&&e.endsWith(t),sn=Object.entries,J1=Object.fromEntries,gt=Object.keys,Q1=e=>(e.endsWith(".md")&&(e=`${e.slice(0,-3)}.html`),!e.endsWith("/")&&!e.endsWith(".html")&&(e=`${e}.html`),e=e.replace(/(^|\/)(?:README|index).html$/i,"$1"),e),tc=e=>{const[t,l=""]=e.split("#");return t?`${Q1(t)}${l?`#${l}`:""}`:e},Qr=e=>ec(e)&&Tt(e.name),Yl=(e,t=!1)=>e?Zo(e)?e.map(l=>Tt(l)?{name:l}:Qr(l)?l:null).filter(l=>l!==null):Tt(e)?[{name:e}]:Qr(e)?[e]:(console.error(`Expect "author" to be \`AuthorInfo[] | AuthorInfo | string[] | string ${t?"":"| false"} | undefined\`, but got`,e),[]):[],lc=(e,t)=>{if(e){if(Zo(e)&&e.every(Tt))return e;if(Tt(e))return[e];console.error(`Expect ${t||"value"} to be \`string[] | string | undefined\`, but got`,e)}return[]},nc=e=>lc(e,"category"),ac=e=>lc(e,"tag"),na=e=>Ql(e,"/");let Y1=class{constructor(){oa(this,"containerElement");oa(this,"messageElements",{});const t="message-container",l=document.getElementById(t);l?this.containerElement=l:(this.containerElement=document.createElement("div"),this.containerElement.id=t,document.body.appendChild(this.containerElement))}pop(t,l=2e3){const n=document.createElement("div"),a=Date.now();return n.className="message move-in",n.innerHTML=t,this.containerElement.appendChild(n),this.messageElements[a]=n,l>0&&setTimeout(()=>{this.close(a)},l),a}close(t){if(t){const l=this.messageElements[t];l.classList.remove("move-in"),l.classList.add("move-out"),l.addEventListener("animationend",()=>{l.remove(),delete this.messageElements[t]})}else gt(this.messageElements).forEach(l=>this.close(Number(l)))}destroy(){document.body.removeChild(this.containerElement)}};const ic=/#.*$/u,X1=e=>{const t=ic.exec(e);return t?t[0]:""},Yr=e=>decodeURI(e).replace(ic,"").replace(/(index)?\.(md|html)$/,""),Ci=(e,t)=>{if(t===void 0)return!1;const l=Yr(e.path),n=Yr(t),a=X1(t);return a?a===e.hash&&(!n||l===n):l===n},Z1=e=>Oi(e)?e:`https://github.com/${e}`,rc=e=>!Oi(e)||/github\.com/.test(e)?"GitHub":/bitbucket\.org/.test(e)?"Bitbucket":/gitlab\.com/.test(e)?"GitLab":/gitee\.com/.test(e)?"Gitee":null,_l=(e,...t)=>{const l=e.resolve(...t),n=l.matched[l.matched.length-1];if(!(n!=null&&n.redirect))return l;const{redirect:a}=n,i=z1(a)?a(l):a,r=H1(i)?{path:i}:i;return _l(e,{hash:l.hash,query:l.query,params:l.params,...r})},eh=e=>{if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget){const t=e.currentTarget.getAttribute("target");if(t!=null&&t.match(/\b_blank\b/i))return}return e.preventDefault(),!0}},Re=({to:e=""},{slots:t})=>{var l;const n=Ve(),a=(i={})=>eh(i)?n.push(e).catch():Promise.resolve();return s("a",{class:"vp-link",href:Le(tc(e)),onClick:a},(l=t.default)==null?void 0:l.call(t))};Re.displayName="VPLink";const sc=()=>s(de,{name:"github"},()=>s("path",{d:"M511.957 21.333C241.024 21.333 21.333 240.981 21.333 512c0 216.832 140.544 400.725 335.574 465.664 24.49 4.395 32.256-10.07 32.256-23.083 0-11.69.256-44.245 0-85.205-136.448 29.61-164.736-64.64-164.736-64.64-22.315-56.704-54.4-71.765-54.4-71.765-44.587-30.464 3.285-29.824 3.285-29.824 49.195 3.413 75.179 50.517 75.179 50.517 43.776 75.008 114.816 53.333 142.762 40.79 4.523-31.66 17.152-53.377 31.19-65.537-108.971-12.458-223.488-54.485-223.488-242.602 0-53.547 19.114-97.323 50.517-131.67-5.035-12.33-21.93-62.293 4.779-129.834 0 0 41.258-13.184 134.912 50.346a469.803 469.803 0 0 1 122.88-16.554c41.642.213 83.626 5.632 122.88 16.554 93.653-63.488 134.784-50.346 134.784-50.346 26.752 67.541 9.898 117.504 4.864 129.834 31.402 34.347 50.474 78.123 50.474 131.67 0 188.586-114.73 230.016-224.042 242.09 17.578 15.232 33.578 44.672 33.578 90.454v135.85c0 13.142 7.936 27.606 32.854 22.87C862.25 912.597 1002.667 728.747 1002.667 512c0-271.019-219.648-490.667-490.71-490.667z"}));sc.displayName="GitHubIcon";const oc=()=>s(de,{name:"gitlab"},()=>s("path",{d:"M229.333 78.688C223.52 62 199.895 62 193.895 78.688L87.958 406.438h247.5c-.188 0-106.125-327.75-106.125-327.75zM33.77 571.438c-4.875 15 .563 31.687 13.313 41.25l464.812 345L87.77 406.438zm301.5-165 176.813 551.25 176.812-551.25zm655.125 165-54-165-424.312 551.25 464.812-345c12.938-9.563 18.188-26.25 13.5-41.25zM830.27 78.688c-5.812-16.688-29.437-16.688-35.437 0l-106.125 327.75h247.5z"}));oc.displayName="GitLabIcon";const cc=()=>s(de,{name:"gitee"},()=>s("path",{d:"M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm242.97-533.34H482.39a23.7 23.7 0 0 0-23.7 23.7l-.03 59.28c0 13.08 10.59 23.7 23.7 23.7h165.96a23.7 23.7 0 0 1 23.7 23.7v11.85a71.1 71.1 0 0 1-71.1 71.1H375.71a23.7 23.7 0 0 1-23.7-23.7V423.11a71.1 71.1 0 0 1 71.1-71.1h331.8a23.7 23.7 0 0 0 23.7-23.7l.06-59.25a23.73 23.73 0 0 0-23.7-23.73H423.11a177.78 177.78 0 0 0-177.78 177.75v331.83c0 13.08 10.62 23.7 23.7 23.7h349.62a159.99 159.99 0 0 0 159.99-159.99V482.33a23.7 23.7 0 0 0-23.7-23.7z"}));cc.displayName="GiteeIcon";const uc=()=>s(de,{name:"bitbucket"},()=>s("path",{d:"M575.256 490.862c6.29 47.981-52.005 85.723-92.563 61.147-45.714-20.004-45.714-92.562-1.133-113.152 38.29-23.442 93.696 7.424 93.696 52.005zm63.451-11.996c-10.276-81.152-102.29-134.839-177.152-101.156-47.433 21.138-79.433 71.424-77.129 124.562 2.853 69.705 69.157 126.866 138.862 120.576S647.3 548.571 638.708 478.83zm136.558-309.723c-25.161-33.134-67.986-38.839-105.728-45.13-106.862-17.151-216.576-17.7-323.438 1.134-35.438 5.706-75.447 11.996-97.719 43.996 36.572 34.304 88.576 39.424 135.424 45.129 84.553 10.862 171.447 11.447 256 .585 47.433-5.705 99.987-10.276 135.424-45.714zm32.585 591.433c-16.018 55.99-6.839 131.438-66.304 163.986-102.29 56.576-226.304 62.867-338.87 42.862-59.43-10.862-129.135-29.696-161.72-85.723-14.3-54.858-23.442-110.848-32.585-166.84l3.438-9.142 10.276-5.157c170.277 112.567 408.576 112.567 579.438 0 26.844 8.01 6.84 40.558 6.29 60.014zm103.424-549.157c-19.42 125.148-41.728 249.71-63.415 374.272-6.29 36.572-41.728 57.162-71.424 72.558-106.862 53.724-231.424 62.866-348.562 50.286-79.433-8.558-160.585-29.696-225.134-79.433-30.28-23.443-30.28-63.415-35.986-97.134-20.005-117.138-42.862-234.277-57.161-352.585 6.839-51.42 64.585-73.728 107.447-89.71 57.16-21.138 118.272-30.866 178.87-36.571 129.134-12.58 261.157-8.01 386.304 28.562 44.581 13.13 92.563 31.415 122.844 69.705 13.714 17.7 9.143 40.01 6.29 60.014z"}));uc.displayName="BitbucketIcon";const dc=()=>s(de,{name:"source"},()=>s("path",{d:"M601.92 475.2c0 76.428-8.91 83.754-28.512 99.594-14.652 11.88-43.956 14.058-78.012 16.434-18.81 1.386-40.392 2.97-62.172 6.534-18.612 2.97-36.432 9.306-53.064 17.424V299.772c37.818-21.978 63.36-62.766 63.36-109.692 0-69.894-56.826-126.72-126.72-126.72S190.08 120.186 190.08 190.08c0 46.926 25.542 87.714 63.36 109.692v414.216c-37.818 21.978-63.36 62.766-63.36 109.692 0 69.894 56.826 126.72 126.72 126.72s126.72-56.826 126.72-126.72c0-31.086-11.286-59.598-29.7-81.576 13.266-9.504 27.522-17.226 39.996-19.206 16.038-2.574 32.868-3.762 50.688-5.148 48.312-3.366 103.158-7.326 148.896-44.55 61.182-49.698 74.25-103.158 75.24-187.902V475.2h-126.72zM316.8 126.72c34.848 0 63.36 28.512 63.36 63.36s-28.512 63.36-63.36 63.36-63.36-28.512-63.36-63.36 28.512-63.36 63.36-63.36zm0 760.32c-34.848 0-63.36-28.512-63.36-63.36s28.512-63.36 63.36-63.36 63.36 28.512 63.36 63.36-28.512 63.36-63.36 63.36zM823.68 158.4h-95.04V63.36h-126.72v95.04h-95.04v126.72h95.04v95.04h126.72v-95.04h95.04z"}));dc.displayName="SourceIcon";const ct=(e,t)=>{const l=t?t._instance:Xt();return ec(l==null?void 0:l.appContext.components)&&(e in l.appContext.components||tt(e)in l.appContext.components||en(tt(e))in l.appContext.components)},th=()=>K1(()=>typeof window<"u"&&window.navigator&&"userAgent"in window.navigator),lh=()=>{const e=th();return E(()=>e.value&&/\b(?:Android|iPhone)/i.test(navigator.userAgent))},on=e=>{const t=mt();return E(()=>e[t.value])};var nh=Object.defineProperty,ah=Object.defineProperties,ih=Object.getOwnPropertyDescriptors,Xr=Object.getOwnPropertySymbols,rh=Object.prototype.hasOwnProperty,sh=Object.prototype.propertyIsEnumerable,Zr=(e,t,l)=>t in e?nh(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l,oh=(e,t)=>{for(var l in t||(t={}))rh.call(t,l)&&Zr(e,l,t[l]);if(Xr)for(var l of Xr(t))sh.call(t,l)&&Zr(e,l,t[l]);return e},ch=(e,t)=>ah(e,ih(t));function es(e,t){var l;const n=Ue();return Xs(()=>{n.value=e()},ch(oh({},t),{flush:(l=t==null?void 0:t.flush)!=null?l:"sync"})),Yt(n)}function Al(e){return Ps()?(td(e),!0):!1}function ze(e){return typeof e=="function"?e():it(e)}const cn=typeof window<"u",Xl=()=>{},ts=uh();function uh(){var e;return cn&&((e=window==null?void 0:window.navigator)==null?void 0:e.userAgent)&&/iP(ad|hone|od)/.test(window.navigator.userAgent)}function pc(e,t){function l(...n){return new Promise((a,i)=>{Promise.resolve(e(()=>t.apply(this,n),{fn:t,thisArg:this,args:n})).then(a).catch(i)})}return l}const hc=e=>e();function dh(e,t=!0,l=!0,n=!1){let a=0,i,r=!0,o=Xl,c;const u=()=>{i&&(clearTimeout(i),i=void 0,o(),o=Xl)};return p=>{const h=ze(e),f=Date.now()-a,b=()=>c=p();return u(),h<=0?(a=Date.now(),b()):(f>h&&(l||!r)?(a=Date.now(),b()):t&&(c=new Promise((w,x)=>{o=n?x:w,i=setTimeout(()=>{a=Date.now(),r=!0,w(b()),u()},Math.max(0,h-f))})),!l&&!i&&(i=setTimeout(()=>r=!0,h)),r=!1,c)}}function ph(e=hc){const t=W(!0);function l(){t.value=!1}function n(){t.value=!0}const a=(...i)=>{t.value&&e(...i)};return{isActive:Yt(t),pause:l,resume:n,eventFilter:a}}function hh(...e){if(e.length!==1)return Ll(...e);const t=e[0];return typeof t=="function"?Yt(Rd(()=>({get:t,set:Xl}))):W(t)}function fh(e,t=200,l=!1,n=!0,a=!1){return pc(dh(t,l,n,a),e)}function fc(e,t=!0){Xt()?ke(e):t?e():Tl(e)}function vh(e){Xt()&&an(e)}function gh(e,t,l={}){const{immediate:n=!0}=l,a=W(!1);let i=null;function r(){i&&(clearTimeout(i),i=null)}function o(){a.value=!1,r()}function c(...u){r(),a.value=!0,i=setTimeout(()=>{a.value=!1,i=null,e(...u)},ze(t))}return n&&(a.value=!0,cn&&c()),Al(o),{isPending:Yt(a),start:c,stop:o}}function ls(e=!1,t={}){const{truthyValue:l=!0,falsyValue:n=!1}=t,a=Oe(e),i=W(e);function r(o){if(arguments.length)return i.value=o,i.value;{const c=ze(l);return i.value=i.value===c?ze(n):c,i.value}}return a?r:[i,r]}var ns=Object.getOwnPropertySymbols,mh=Object.prototype.hasOwnProperty,yh=Object.prototype.propertyIsEnumerable,bh=(e,t)=>{var l={};for(var n in e)mh.call(e,n)&&t.indexOf(n)<0&&(l[n]=e[n]);if(e!=null&&ns)for(var n of ns(e))t.indexOf(n)<0&&yh.call(e,n)&&(l[n]=e[n]);return l};function _h(e,t,l={}){const n=l,{eventFilter:a=hc}=n,i=bh(n,["eventFilter"]);return ce(e,pc(a,t),i)}var kh=Object.defineProperty,wh=Object.defineProperties,Eh=Object.getOwnPropertyDescriptors,Hn=Object.getOwnPropertySymbols,vc=Object.prototype.hasOwnProperty,gc=Object.prototype.propertyIsEnumerable,as=(e,t,l)=>t in e?kh(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l,xh=(e,t)=>{for(var l in t||(t={}))vc.call(t,l)&&as(e,l,t[l]);if(Hn)for(var l of Hn(t))gc.call(t,l)&&as(e,l,t[l]);return e},Lh=(e,t)=>wh(e,Eh(t)),Th=(e,t)=>{var l={};for(var n in e)vc.call(e,n)&&t.indexOf(n)<0&&(l[n]=e[n]);if(e!=null&&Hn)for(var n of Hn(e))t.indexOf(n)<0&&gc.call(e,n)&&(l[n]=e[n]);return l};function Ah(e,t,l={}){const n=l,{eventFilter:a}=n,i=Th(n,["eventFilter"]),{eventFilter:r,pause:o,resume:c,isActive:u}=ph(a);return{stop:_h(e,t,Lh(xh({},i),{eventFilter:r})),pause:o,resume:c,isActive:u}}function Ft(e){var t;const l=ze(e);return(t=l==null?void 0:l.$el)!=null?t:l}const zt=cn?window:void 0,mc=cn?window.document:void 0,Ih=cn?window.navigator:void 0;function Me(...e){let t,l,n,a;if(typeof e[0]=="string"||Array.isArray(e[0])?([l,n,a]=e,t=zt):[t,l,n,a]=e,!t)return Xl;Array.isArray(l)||(l=[l]),Array.isArray(n)||(n=[n]);const i=[],r=()=>{i.forEach(d=>d()),i.length=0},o=(d,p,h,f)=>(d.addEventListener(p,h,f),()=>d.removeEventListener(p,h,f)),c=ce(()=>[Ft(t),ze(a)],([d,p])=>{r(),d&&i.push(...l.flatMap(h=>n.map(f=>o(d,h,f,p))))},{immediate:!0,flush:"post"}),u=()=>{c(),r()};return Al(u),u}function Ph(){const e=W(!1);return Xt()&&ke(()=>{e.value=!0}),e}function aa(e){const t=Ph();return E(()=>(t.value,!!e()))}function yc(e,t={}){const{window:l=zt}=t,n=aa(()=>l&&"matchMedia"in l&&typeof l.matchMedia=="function");let a;const i=W(!1),r=u=>{i.value=u.matches},o=()=>{a&&("removeEventListener"in a?a.removeEventListener("change",r):a.removeListener(r))},c=Xs(()=>{n.value&&(o(),a=l.matchMedia(ze(e)),"addEventListener"in a?a.addEventListener("change",r):a.addListener(r),i.value=a.matches)});return Al(()=>{c(),o(),a=void 0}),i}function Oh(e={}){const{navigator:t=Ih,read:l=!1,source:n,copiedDuring:a=1500,legacy:i=!1}=e,r=aa(()=>t&&"clipboard"in t),o=E(()=>r.value||i),c=W(""),u=W(!1),d=gh(()=>u.value=!1,a);function p(){r.value?t.clipboard.readText().then(w=>{c.value=w}):c.value=b()}o.value&&l&&Me(["copy","cut"],p);async function h(w=ze(n)){o.value&&w!=null&&(r.value?await t.clipboard.writeText(w):f(w),c.value=w,u.value=!0,d.start())}function f(w){const x=document.createElement("textarea");x.value=w??"",x.style.position="absolute",x.style.opacity="0",document.body.appendChild(x),x.select(),document.execCommand("copy"),x.remove()}function b(){var w,x,g;return(g=(x=(w=document==null?void 0:document.getSelection)==null?void 0:w.call(document))==null?void 0:x.toString())!=null?g:""}return{isSupported:o,text:c,copied:u,copy:h}}const Tn=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},An="__vueuse_ssr_handlers__",Ch=Rh();function Rh(){return An in Tn||(Tn[An]=Tn[An]||{}),Tn[An]}function Sh(e,t){return Ch[e]||t}function Dh(e){return e==null?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":typeof e=="boolean"?"boolean":typeof e=="string"?"string":typeof e=="object"?"object":Number.isNaN(e)?"any":"number"}var $h=Object.defineProperty,is=Object.getOwnPropertySymbols,Mh=Object.prototype.hasOwnProperty,Vh=Object.prototype.propertyIsEnumerable,rs=(e,t,l)=>t in e?$h(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l,ss=(e,t)=>{for(var l in t||(t={}))Mh.call(t,l)&&rs(e,l,t[l]);if(is)for(var l of is(t))Vh.call(t,l)&&rs(e,l,t[l]);return e};const Fh={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}},os="vueuse-storage";function Ri(e,t,l,n={}){var a;const{flush:i="pre",deep:r=!0,listenToStorageChanges:o=!0,writeDefaults:c=!0,mergeDefaults:u=!1,shallow:d,window:p=zt,eventFilter:h,onError:f=T=>{console.error(T)}}=n,b=(d?Ue:W)(t);if(!l)try{l=Sh("getDefaultStorage",()=>{var T;return(T=zt)==null?void 0:T.localStorage})()}catch(T){f(T)}if(!l)return b;const w=ze(t),x=Dh(w),g=(a=n.serializer)!=null?a:Fh[x],{pause:_,resume:P}=Ah(b,()=>O(b.value),{flush:i,deep:r,eventFilter:h});return p&&o&&(Me(p,"storage",V),Me(p,os,C)),V(),b;function O(T){try{if(T==null)l.removeItem(e);else{const q=g.write(T),Y=l.getItem(e);Y!==q&&(l.setItem(e,q),p&&p.dispatchEvent(new CustomEvent(os,{detail:{key:e,oldValue:Y,newValue:q,storageArea:l}})))}}catch(q){f(q)}}function B(T){const q=T?T.newValue:l.getItem(e);if(q==null)return c&&w!==null&&l.setItem(e,g.write(w)),w;if(!T&&u){const Y=g.read(q);return typeof u=="function"?u(Y,w):x==="object"&&!Array.isArray(Y)?ss(ss({},w),Y):Y}else return typeof q!="string"?q:g.read(q)}function C(T){V(T.detail)}function V(T){if(!(T&&T.storageArea!==l)){if(T&&T.key==null){b.value=w;return}if(!(T&&T.key!==e)){_();try{b.value=B(T)}catch(q){f(q)}finally{T?Tl(P):P()}}}}}function Nh(e){return yc("(prefers-color-scheme: dark)",e)}var cs=Object.getOwnPropertySymbols,jh=Object.prototype.hasOwnProperty,Bh=Object.prototype.propertyIsEnumerable,zh=(e,t)=>{var l={};for(var n in e)jh.call(e,n)&&t.indexOf(n)<0&&(l[n]=e[n]);if(e!=null&&cs)for(var n of cs(e))t.indexOf(n)<0&&Bh.call(e,n)&&(l[n]=e[n]);return l};function Hh(e,t,l={}){const n=l,{window:a=zt}=n,i=zh(n,["window"]);let r;const o=aa(()=>a&&"ResizeObserver"in a),c=()=>{r&&(r.disconnect(),r=void 0)},u=E(()=>Array.isArray(e)?e.map(h=>Ft(h)):[Ft(e)]),d=ce(u,h=>{if(c(),o.value&&a){r=new ResizeObserver(t);for(const f of h)f&&r.observe(f,i)}},{immediate:!0,flush:"post",deep:!0}),p=()=>{c(),d()};return Al(p),{isSupported:o,stop:p}}function qh(e,t={width:0,height:0},l={}){const{window:n=zt,box:a="content-box"}=l,i=E(()=>{var c,u;return(u=(c=Ft(e))==null?void 0:c.namespaceURI)==null?void 0:u.includes("svg")}),r=W(t.width),o=W(t.height);return Hh(e,([c])=>{const u=a==="border-box"?c.borderBoxSize:a==="content-box"?c.contentBoxSize:c.devicePixelContentBoxSize;if(n&&i.value){const d=Ft(e);if(d){const p=n.getComputedStyle(d);r.value=Number.parseFloat(p.width),o.value=Number.parseFloat(p.height)}}else if(u){const d=Array.isArray(u)?u:[u];r.value=d.reduce((p,{inlineSize:h})=>p+h,0),o.value=d.reduce((p,{blockSize:h})=>p+h,0)}else r.value=c.contentRect.width,o.value=c.contentRect.height},l),ce(()=>Ft(e),c=>{r.value=c?t.width:0,o.value=c?t.height:0}),{width:r,height:o}}const us=["fullscreenchange","webkitfullscreenchange","webkitendfullscreen","mozfullscreenchange","MSFullscreenChange"];function Si(e,t={}){const{document:l=mc,autoExit:n=!1}=t,a=E(()=>{var g;return(g=Ft(e))!=null?g:l==null?void 0:l.querySelector("html")}),i=W(!1),r=E(()=>["requestFullscreen","webkitRequestFullscreen","webkitEnterFullscreen","webkitEnterFullScreen","webkitRequestFullScreen","mozRequestFullScreen","msRequestFullscreen"].find(g=>l&&g in l||a.value&&g in a.value)),o=E(()=>["exitFullscreen","webkitExitFullscreen","webkitExitFullScreen","webkitCancelFullScreen","mozCancelFullScreen","msExitFullscreen"].find(g=>l&&g in l||a.value&&g in a.value)),c=E(()=>["fullScreen","webkitIsFullScreen","webkitDisplayingFullscreen","mozFullScreen","msFullscreenElement"].find(g=>l&&g in l||a.value&&g in a.value)),u=["fullscreenElement","webkitFullscreenElement","mozFullScreenElement","msFullscreenElement"].find(g=>l&&g in l),d=aa(()=>a.value&&l&&r.value!==void 0&&o.value!==void 0&&c.value!==void 0),p=()=>u?(l==null?void 0:l[u])===a.value:!1,h=()=>{if(c.value){if(l&&l[c.value]!=null)return l[c.value];{const g=a.value;if((g==null?void 0:g[c.value])!=null)return!!g[c.value]}}return!1};async function f(){if(!(!d.value||!i.value)){if(o.value)if((l==null?void 0:l[o.value])!=null)await l[o.value]();else{const g=a.value;(g==null?void 0:g[o.value])!=null&&await g[o.value]()}i.value=!1}}async function b(){if(!d.value||i.value)return;h()&&await f();const g=a.value;r.value&&(g==null?void 0:g[r.value])!=null&&(await g[r.value](),i.value=!0)}async function w(){await(i.value?f():b())}const x=()=>{const g=h();(!g||g&&p())&&(i.value=g)};return Me(l,us,x,!1),Me(()=>Ft(a),us,x,!1),n&&Al(f),{isSupported:d,isFullscreen:i,enter:b,exit:f,toggle:w}}function wa(e,t=Xl,l={}){const{immediate:n=!0,manual:a=!1,type:i="text/javascript",async:r=!0,crossOrigin:o,referrerPolicy:c,noModule:u,defer:d,document:p=mc,attrs:h={}}=l,f=W(null);let b=null;const w=_=>new Promise((P,O)=>{const B=T=>(f.value=T,P(T),T);if(!p){P(!1);return}let C=!1,V=p.querySelector(`script[src="${ze(e)}"]`);V?V.hasAttribute("data-loaded")&&B(V):(V=p.createElement("script"),V.type=i,V.async=r,V.src=ze(e),d&&(V.defer=d),o&&(V.crossOrigin=o),u&&(V.noModule=u),c&&(V.referrerPolicy=c),Object.entries(h).forEach(([T,q])=>V==null?void 0:V.setAttribute(T,q)),C=!0),V.addEventListener("error",T=>O(T)),V.addEventListener("abort",T=>O(T)),V.addEventListener("load",()=>{V.setAttribute("data-loaded","true"),t(V),B(V)}),C&&(V=p.head.appendChild(V)),_||B(V)}),x=(_=!0)=>(b||(b=w(_)),b),g=()=>{if(!p)return;b=null,f.value&&(f.value=null);const _=p.querySelector(`script[src="${ze(e)}"]`);_&&p.head.removeChild(_)};return n&&!a&&fc(x),a||vh(g),{scriptTag:f,load:x,unload:g}}function bc(e){const t=window.getComputedStyle(e);if(t.overflowX==="scroll"||t.overflowY==="scroll"||t.overflowX==="auto"&&e.clientWidth1?!0:(t.preventDefault&&t.preventDefault(),!1)}function _c(e,t=!1){const l=W(t);let n=null,a;ce(hh(e),o=>{if(o){const c=o;a=c.style.overflow,l.value&&(c.style.overflow="hidden")}},{immediate:!0});const i=()=>{const o=ze(e);!o||l.value||(ts&&(n=Me(o,"touchmove",c=>{Uh(c)},{passive:!1})),o.style.overflow="hidden",l.value=!0)},r=()=>{const o=ze(e);!o||!l.value||(ts&&(n==null||n()),o.style.overflow=a,l.value=!1)};return Al(r),E({get(){return l.value},set(o){o?i():r()}})}function Gh({window:e=zt}={}){if(!e)return{x:W(0),y:W(0)};const t=W(e.scrollX),l=W(e.scrollY);return Me(e,"scroll",()=>{t.value=e.scrollX,l.value=e.scrollY},{capture:!1,passive:!0}),{x:t,y:l}}function Wh(e={}){const{window:t=zt,initialWidth:l=Number.POSITIVE_INFINITY,initialHeight:n=Number.POSITIVE_INFINITY,listenOrientation:a=!0,includeScrollbar:i=!0}=e,r=W(l),o=W(n),c=()=>{t&&(i?(r.value=t.innerWidth,o.value=t.innerHeight):(r.value=t.document.documentElement.clientWidth,o.value=t.document.documentElement.clientHeight))};if(c(),fc(c),Me("resize",c,{passive:!0}),a){const u=yc("(orientation: portrait)");ce(u,()=>c())}return{width:r,height:o}}var Kh=M({name:"FontIcon",props:{icon:{type:String,default:""},color:{type:String,default:""},size:{type:[String,Number],default:""}},setup(e){const t=E(()=>{const n=["font-icon icon"],a=`fas fa-${e.icon}`;return n.push("fa-fw fa-sm"),n.push(e.icon.includes(" ")?e.icon:a),n}),l=E(()=>{const n={};return e.color&&(n.color=e.color),e.size&&(n["font-size"]=Number.isNaN(Number(e.size))?e.size:`${e.size}px`),gt(n).length?n:null});return()=>e.icon?s("span",{key:e.icon,class:t.value,style:l.value}):null}});const Jh="accelerometer; autoplay; clipboard-write; encrypted-media; fullscreen; gyroscope; picture-in-picture",ds=e=>ae(e)?e:`${e}px`,Qh=(e,t=0)=>{const l=Ue(),n=E(()=>ds(it(e.width)||"100%")),a=W("auto"),i=c=>{if(ae(c)){const[u,d]=c.split(":"),p=Number(u)/Number(d);if(!Number.isNaN(p))return p}return typeof c=="number"?c:16/9},r=c=>{const u=it(e.height),d=i(it(e.ratio));return u?ds(u):`${Number(c)/d+it(t)}px`},o=()=>{l.value&&(a.value=r(l.value.clientWidth))};return ke(()=>{o(),Oe(t)&&ce(t,()=>o()),Me("orientationchange",()=>o()),Me("resize",()=>o())}),{el:l,width:n,height:a}},ps="https://player.bilibili.com/player.html";var Yh=M({name:"BiliBili",props:{bvid:{type:String,default:""},aid:{type:String,default:""},cid:{type:String,default:""},title:{type:String,default:"A BiliBili video"},page:{type:[String,Number],default:1},width:{type:[String,Number],default:"100%"},height:{type:[String,Number],default:void 0},ratio:{type:[String,Number],default:16/9},time:{type:[String,Number],default:0},autoplay:Boolean},setup(e){const{el:t,width:l,height:n}=Qh(e),a=W(!1),i=E(()=>{const{aid:r,bvid:o,cid:c,autoplay:u,time:d,page:p}=e;return r&&c?`${ps}?aid=${r}&cid=${c}&t=${d}&autoplay=${u?1:0}&page=${p}`:o?`${ps}?bvid=${o}&t=${d}&autoplay=${u?1:0}`:null});return()=>i.value?[s("div",{class:"bilibili-desc"},s("a",{class:"sr-only",href:i.value},e.title)),s("iframe",{ref:t,src:i.value,title:e.title,class:"bilibili-iframe",allow:Jh,style:{width:l.value,height:a.value?n.value:0},onLoad:()=>{a.value=!0}}),a.value?null:s(Yo)]:[]}});const kc=()=>s(de,{name:"back-to-top"},()=>[s("path",{d:"M512 843.2c-36.2 0-66.4-13.6-85.8-21.8-10.8-4.6-22.6 3.6-21.8 15.2l7 102c.4 6.2 7.6 9.4 12.6 5.6l29-22c3.6-2.8 9-1.8 11.4 2l41 64.2c3 4.8 10.2 4.8 13.2 0l41-64.2c2.4-3.8 7.8-4.8 11.4-2l29 22c5 3.8 12.2.6 12.6-5.6l7-102c.8-11.6-11-20-21.8-15.2-19.6 8.2-49.6 21.8-85.8 21.8z"}),s("path",{d:"m795.4 586.2-96-98.2C699.4 172 513 32 513 32S324.8 172 324.8 488l-96 98.2c-3.6 3.6-5.2 9-4.4 14.2L261.2 824c1.8 11.4 14.2 17 23.6 10.8L419 744s41.4 40 94.2 40c52.8 0 92.2-40 92.2-40l134.2 90.8c9.2 6.2 21.6.6 23.6-10.8l37-223.8c.4-5.2-1.2-10.4-4.8-14zM513 384c-34 0-61.4-28.6-61.4-64s27.6-64 61.4-64c34 0 61.4 28.6 61.4 64S547 384 513 384z"})]);kc.displayName="BackToTopIcon";var Xh=M({name:"BackToTop",props:{threshold:{type:Number,default:100},noProgress:Boolean},setup(e){const t=Ee(),l=on({"/":{backToTop:"返回顶部"}}),n=Ue(),{height:a}=qh(n),{height:i}=Wh(),{y:r}=Gh(),o=E(()=>t.value.backToTop!==!1&&r.value>e.threshold),c=E(()=>r.value/(a.value-i.value));return ke(()=>{n.value=document.body}),()=>s(Bt,{name:"fade"},()=>o.value?s("button",{type:"button",class:"vp-back-to-top-button","aria-label":l.value.backToTop,"data-balloon-pos":"left",onClick:()=>{window.scrollTo({top:0,behavior:"smooth"})}},[e.noProgress?null:s("svg",{class:"vp-scroll-progress"},s("circle",{cx:"50%",cy:"50%",style:{"stroke-dasharray":`calc(${Math.PI*c.value*100}% - ${4*Math.PI}px) calc(${Math.PI*100}% - ${4*Math.PI}px)`}})),s(kc)]):null)}});const Zh=dt({enhance:({app:e})=>{ct("FontIcon")||e.component("FontIcon",Kh),ct("BiliBili")||e.component("BiliBili",Yh)},setup:()=>{wa("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/js/brands.min.js",()=>{},{attrs:{"data-auto-replace-svg":"nest"}}),wa("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/js/solid.min.js",()=>{},{attrs:{"data-auto-replace-svg":"nest"}}),wa("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/js/fontawesome.min.js",()=>{},{attrs:{"data-auto-replace-svg":"nest"}})},rootComponents:[()=>s(Xh,{})]});function ef(e,t,l){var n,a,i;t===void 0&&(t=50),l===void 0&&(l={});var r=(n=l.isImmediate)!=null&&n,o=(a=l.callback)!=null&&a,c=l.maxWait,u=Date.now(),d=[];function p(){if(c!==void 0){var f=Date.now()-u;if(f+t>=c)return c-f}return t}var h=function(){var f=[].slice.call(arguments),b=this;return new Promise(function(w,x){var g=r&&i===void 0;if(i!==void 0&&clearTimeout(i),i=setTimeout(function(){if(i=void 0,u=Date.now(),!r){var P=e.apply(b,f);o&&o(P),d.forEach(function(O){return(0,O.resolve)(P)}),d=[]}},p()),g){var _=e.apply(b,f);return o&&o(_),w(_)}d.push({resolve:w,reject:x})})};return h.cancel=function(f){i!==void 0&&clearTimeout(i),d.forEach(function(b){return(0,b.reject)(f)}),d=[]},h}const tf=({headerLinkSelector:e,headerAnchorSelector:t,delay:l,offset:n=5})=>{const a=Ve(),r=ef(()=>{var w,x;const o=Math.max(window.scrollY,document.documentElement.scrollTop,document.body.scrollTop);if(Math.abs(o-0)h.some(_=>_.hash===g.hash));for(let g=0;g=(((w=_.parentElement)==null?void 0:w.offsetTop)??0)-n,B=!P||o<(((x=P.parentElement)==null?void 0:x.offsetTop)??0)-n;if(!(O&&B))continue;const V=decodeURIComponent(a.currentRoute.value.hash),T=decodeURIComponent(_.hash);if(V===T)return;if(p){for(let q=g+1;q{window.addEventListener("scroll",r)}),Yn(()=>{window.removeEventListener("scroll",r)})},hs=async(e,t)=>{const{scrollBehavior:l}=e.options;e.options.scrollBehavior=void 0,await e.replace({query:e.currentRoute.value.query,hash:t}).finally(()=>e.options.scrollBehavior=l)},lf=".vp-sidebar-link, .toc-link",nf=".header-anchor",af=200,rf=5,sf=dt({setup(){tf({headerLinkSelector:lf,headerAnchorSelector:nf,delay:af,offset:rf})}});let wc=()=>null;const Ec=Symbol(""),of=e=>{wc=e},cf=()=>me(Ec),uf=e=>{e.provide(Ec,wc)};var df=M({name:"AutoCatalog",props:{base:{type:String,default:""},level:{type:Number,default:3},index:Boolean},setup(e){const t=cf(),l=on({"/":{title:"目录",empty:"暂无目录"}}),n=ue(),a=Ve(),i=Vo(),r=u=>{const d=u.I;return typeof d>"u"||d},o=()=>{const u=e.base||n.value.path.replace(/\/[^/]+$/,"/"),d=a.getRoutes(),p=[];return d.filter(({meta:h,path:f})=>{if(!Ql(f,u)||f===u)return!1;if(u==="/"){const b=gt(i.value.locales).filter(w=>w!=="/");if(f==="/404.html"||b.some(w=>Ql(f,w)))return!1}return(il(f,".html")&&!il(f,"/index.html")||il(f,"/"))&&r(h)}).map(({path:h,meta:f})=>{const b=h.substring(u.length).split("/").length;return{title:f.t||"",icon:f.i,base:h.replace(/\/[^/]+\/?$/,"/"),order:f.O||null,level:il(h,"/")?b-1:b,path:h}}).filter(({title:h,level:f})=>h&&f<=e.level).sort(({title:h,level:f,path:b,order:w},{title:x,level:g,path:_,order:P})=>f-g||(il(b,"/index.html")?-1:il(_,"/index.html")?1:w===null?P===null?h.localeCompare(x):P:P===null?w:w>0?P>0?w-P:-1:P<0?w-P:1)).forEach(h=>{var f;const{base:b,level:w}=h;switch(w){case 1:p.push(h);break;case 2:{const x=p.find(g=>g.path===b);x&&(x.children??(x.children=[])).push(h);break}default:{const x=p.find(g=>g.path===b.replace(/\/[^/]+\/$/,"/"));if(x){const g=(f=x.children)==null?void 0:f.find(_=>_.path===b);g&&(g.children??(g.children=[])).push(h)}}}}),p},c=E(()=>o());return()=>s("div",{class:"vp-catalog"},[s("h2",{class:"vp-catalog-main-title"},l.value.title),c.value.length?c.value.map(({children:u=[],icon:d,path:p,title:h},f)=>[s("h3",{id:h,class:["vp-catalog-child-title",{"has-children":u.length}]},[s("a",{href:`#${h}`,class:"header-anchor","aria-hidden":!0},"#"),s(Re,{class:"vp-catalog-title",to:p},()=>[e.index?`${f+1}.`:null,d&&t?s(t,{icon:d}):null,h||p])]),u.length?s("ul",{class:"vp-catalog-child-catalogs"},u.map(({children:b=[],icon:w,path:x,title:g},_)=>s("li",{class:"vp-child-catalog"},[s("div",{class:["vp-catalog-sub-title",{"has-children":b.length}]},[s("a",{href:`#${g}`,class:"header-anchor"},"#"),s(Re,{class:"vp-catalog-title",to:x},()=>[e.index?`${f+1}.${_+1}`:null,w&&t?s(t,{icon:w}):null,g||x])]),b.length?s("div",{class:"v-sub-catalogs"},b.map(({icon:P,path:O,title:B},C)=>s(Re,{class:"vp-sub-catalog",to:O},()=>[e.index?`${f+1}.${_+1}.${C+1}`:null,P&&t?s(t,{icon:P}):null,B||O]))):null]))):null]):s("p",{class:"vp-empty-catalog"},l.value.empty)])}}),pf=dt({enhance:({app:e})=>{uf(e),ct("AutoCatalog",e)||e.component("AutoCatalog",df)}});const hf=s("svg",{class:"external-link-icon",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",x:"0px",y:"0px",viewBox:"0 0 100 100",width:"15",height:"15"},[s("path",{fill:"currentColor",d:"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"}),s("polygon",{fill:"currentColor",points:"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"})]),xc=M({name:"ExternalLinkIcon",props:{locales:{type:Object,required:!1,default:()=>({})}},setup(e){const t=mt(),l=E(()=>e.locales[t.value]??{openInNewWindow:"open in new window"});return()=>s("span",[hf,s("span",{class:"external-link-icon-sr-only"},l.value.openInNewWindow)])}}),ff={},vf=dt({enhance({app:e}){e.component("ExternalLinkIcon",s(xc,{locales:ff}))}});/** - * NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress - * @license MIT - */const se={settings:{minimum:.08,easing:"ease",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,barSelector:'[role="bar"]',parent:"body",template:'
'},status:null,set:e=>{const t=se.isStarted();e=Ea(e,se.settings.minimum,1),se.status=e===1?null:e;const l=se.render(!t),n=l.querySelector(se.settings.barSelector),a=se.settings.speed,i=se.settings.easing;return l.offsetWidth,gf(r=>{In(n,{transform:"translate3d("+fs(e)+"%,0,0)",transition:"all "+a+"ms "+i}),e===1?(In(l,{transition:"none",opacity:"1"}),l.offsetWidth,setTimeout(function(){In(l,{transition:"all "+a+"ms linear",opacity:"0"}),setTimeout(function(){se.remove(),r()},a)},a)):setTimeout(()=>r(),a)}),se},isStarted:()=>typeof se.status=="number",start:()=>{se.status||se.set(0);const e=()=>{setTimeout(()=>{se.status&&(se.trickle(),e())},se.settings.trickleSpeed)};return se.settings.trickle&&e(),se},done:e=>!e&&!se.status?se:se.inc(.3+.5*Math.random()).set(1),inc:e=>{let t=se.status;return t?(typeof e!="number"&&(e=(1-t)*Ea(Math.random()*t,.1,.95)),t=Ea(t+e,0,.994),se.set(t)):se.start()},trickle:()=>se.inc(Math.random()*se.settings.trickleRate),render:e=>{if(se.isRendered())return document.getElementById("nprogress");vs(document.documentElement,"nprogress-busy");const t=document.createElement("div");t.id="nprogress",t.innerHTML=se.settings.template;const l=t.querySelector(se.settings.barSelector),n=e?"-100":fs(se.status||0),a=document.querySelector(se.settings.parent);return In(l,{transition:"all 0 linear",transform:"translate3d("+n+"%,0,0)"}),a!==document.body&&vs(a,"nprogress-custom-parent"),a==null||a.appendChild(t),t},remove:()=>{gs(document.documentElement,"nprogress-busy"),gs(document.querySelector(se.settings.parent),"nprogress-custom-parent");const e=document.getElementById("nprogress");e&&mf(e)},isRendered:()=>!!document.getElementById("nprogress")},Ea=(e,t,l)=>el?l:e,fs=e=>(-1+e)*100,gf=function(){const e=[];function t(){const l=e.shift();l&&l(t)}return function(l){e.push(l),e.length===1&&t()}}(),In=function(){const e=["Webkit","O","Moz","ms"],t={};function l(r){return r.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(o,c){return c.toUpperCase()})}function n(r){const o=document.body.style;if(r in o)return r;let c=e.length;const u=r.charAt(0).toUpperCase()+r.slice(1);let d;for(;c--;)if(d=e[c]+u,d in o)return d;return r}function a(r){return r=l(r),t[r]??(t[r]=n(r))}function i(r,o,c){o=a(o),r.style[o]=c}return function(r,o){for(const c in o){const u=o[c];u!==void 0&&Object.prototype.hasOwnProperty.call(o,c)&&i(r,c,u)}}}(),Lc=(e,t)=>(typeof e=="string"?e:Di(e)).indexOf(" "+t+" ")>=0,vs=(e,t)=>{const l=Di(e),n=l+t;Lc(l,t)||(e.className=n.substring(1))},gs=(e,t)=>{const l=Di(e);if(!Lc(e,t))return;const n=l.replace(" "+t+" "," ");e.className=n.substring(1,n.length-1)},Di=e=>(" "+(e.className||"")+" ").replace(/\s+/gi," "),mf=e=>{e&&e.parentNode&&e.parentNode.removeChild(e)};const yf=()=>{ke(()=>{const e=Ve(),t=new Set;t.add(e.currentRoute.value.path),e.beforeEach(l=>{t.has(l.path)||se.start()}),e.afterEach(l=>{t.add(l.path),se.done()})})},bf=dt({setup(){yf()}}),_f=JSON.parse('{"encrypt":{},"author":{"name":"levy"},"pageInfo":["Date","Tag"],"logo":"/logo.png","repo":"levy9527/blog","docsDir":"src","displayFooter":true,"editLink":false,"darkmode":"disable","blog":{"description":"Javascript/Java/Python\\n都能撸的“全干工程师”","avatar":"https://avatars.githubusercontent.com/u/9384365?v=4","roundAvatar":true,"intro":"/about.html","medias":{"GitHub":"https://github.com/levy9527","BiliBili":"https://search.bilibili.com/video?keyword=levydotvip&from_source=webtop_search","Email":"mailto:info@897895407@qq.com","Rss":"https://levy.vip/rss.xml"}},"locales":{"/":{"lang":"zh-CN","navbarLocales":{"langName":"简体中文","selectLangAriaLabel":"选择语言"},"metaLocales":{"author":"作者","date":"写作日期","origin":"原创","views":"访问量","category":"分类","tag":"标签","readingTime":"阅读时间","words":"字数","toc":"此页内容","prev":"上一页","next":"下一页","lastUpdated":"上次编辑于","contributors":"贡献者","editLink":"编辑此页","print":"打印"},"blogLocales":{"article":"文章","articleList":"文章列表","category":"分类","tag":"标签","timeline":"时间轴","timelineTitle":"昨日不在","all":"全部","intro":"个人介绍","star":"收藏"},"paginationLocales":{"prev":"上一页","next":"下一页","navigate":"跳转到","action":"前往","errorText":"请输入 1 到 $page 之前的页码!"},"outlookLocales":{"themeColor":"主题色","darkmode":"外观","fullscreen":"全屏"},"routeLocales":{"skipToContent":"跳至主要內容","notFoundTitle":"页面不存在","notFoundMsg":["这里什么也没有","我们是怎么来到这儿的?","这 是 四 零 四 !","看起来你访问了一个失效的链接"],"back":"返回上一页","home":"带我回家","openInNewWindow":"Open in new window"},"navbar":[{"text":"首页","icon":"house","link":"/"},{"text":"分类","icon":"list","children":[{"text":"软件研发","icon":"code","link":"/tools/how-to-connect-to-internet"},{"text":"工作日常","icon":"briefcase","link":"/daily"},{"text":"英语学习","icon":"globe","link":"/english"}]},"/about"],"sidebar":{"/daily/":"structure","/english":["let-chatgpt-be-your-foreign-language-teacher","how-to-self-evaluate-english-level","learning-7000-words-task-completed","everyone-can-learn-english-1-overview","everyone-can-learn-english-2-pronunciation","everyone-can-learn-english-3-words","everyone-can-learn-english-4-listening-and-speaking","everyone-can-learn-english-5-reading-and-writing","contemporary-college-english-1","contemporary-college-english-2","contemporary-college-english-3","contemporary-college-english-4","contemporary-college-english-5","contemporary-college-english-6"],"/":[{"text":"实用工具","prefix":"/tools/","children":["how-to-connect-to-internet"]},{"text":"Git相关","prefix":"/git/","children":["git-useful-commands","git-definitive-guide-to-merge-code","git-history-two-tricks-in-idea","gitlab-ci","rethinking-git-flow","git-best-pratices","use-command-line-tool-to-manage-gitlab-merge-request"]},{"text":"软件测试","prefix":"/software-testing/","children":["unit-testing-overview","use-postman-for-api-testing","use-RestAssured-for-api-testing","use-jest-for-test-driven-development","use-playwright-for-ui-testing","use-cypress-for-e2e-testing"]},{"text":"MySQL","prefix":"/mysql","children":["mysql-backup-case-study-mysqldump-in-action","mysql-data-migration-case-study-add-auto-increment","mysql-details-you-should-know-when-execute-sql-in-command-line"]},{"text":"Java","prefix":"/java","children":["Resolving-Common-Problems-in-IntelliJ-IDEA","Resolving-Common-Problems-in-Maven.md","avoid-sending-password-in-plaintext","recommend-practices-for-query-by-date-range","common-practices-for-handling-excel","check-if-name-exists","how-to-convert-snapshot-into-release-jar-without-source-code","why-i-prefer-fastjson-instead-of-jackson","why-is-it-so-hard-to-upgrade-dependencies"]},{"text":"Python","prefix":"/python","children":["export-mysql-table-into-excel"]},{"text":"前端技术","prefix":"/frontend","children":["old-articles","performance-optimization-in-action"]}]}}}}'),kf=W(_f),Tc=()=>kf,Ac=Symbol(""),wf=()=>{const e=me(Ac);if(!e)throw new Error("useThemeLocaleData() is called without provider.");return e},Ef=(e,t)=>{const{locales:l,...n}=e;return{...n,...l==null?void 0:l[t]}},xf=dt({enhance({app:e}){const t=Tc(),l=e._context.provides[Ti],n=E(()=>Ef(t.value,l.value));e.provide(Ac,n),Object.defineProperties(e.config.globalProperties,{$theme:{get(){return t.value}},$themeLocale:{get(){return n.value}}})}});const Lf=800,Tf=2e3,Af={"/":{copy:"复制代码",copied:"已复制",hint:"复制成功"}},If=!1,Pf=['.theme-hope-content div[class*="language-"] pre'],ms=!1,xa=new Map,Of=()=>{const{copy:e}=Oh({legacy:!0}),t=on(Af),l=ue(),n=lh(),a=o=>{if(!o.hasAttribute("copy-code-registered")){const c=document.createElement("button");c.type="button",c.classList.add("copy-code-button"),c.innerHTML='
',c.setAttribute("aria-label",t.value.copy),c.setAttribute("data-copied",t.value.copied),o.parentElement&&o.parentElement.insertBefore(c,o),o.setAttribute("copy-code-registered","")}},i=()=>Tl().then(()=>new Promise(o=>{setTimeout(()=>{Pf.forEach(c=>{document.querySelectorAll(c).forEach(a)}),o()},Lf)})),r=(o,c,u)=>{let{innerText:d=""}=c;/language-(shellscript|shell|bash|sh|zsh)/.test(o.classList.toString())&&(d=d.replace(/^ *(\$|>) /gm,"")),e(d).then(()=>{u.classList.add("copied"),clearTimeout(xa.get(u));const p=setTimeout(()=>{u.classList.remove("copied"),u.blur(),xa.delete(u)},Tf);xa.set(u,p)})};ke(()=>{(!n.value||ms)&&i(),Me("click",o=>{const c=o.target;if(c.matches('div[class*="language-"] > button.copy')){const u=c.parentElement,d=c.nextElementSibling;d&&r(u,d,c)}else if(c.matches('div[class*="language-"] div.copy-icon')){const u=c.parentElement,d=u.parentElement,p=u.nextElementSibling;p&&r(d,p,u)}}),ce(()=>l.value.path,()=>{(!n.value||ms)&&i()})})};var Cf=dt({setup:()=>{Of()}});const Pn=Ri("VUEPRESS_CODE_TAB_STORE",{});var Rf=M({name:"CodeTabs",props:{active:{type:Number,default:0},data:{type:Array,required:!0},id:{type:String,required:!0},tabId:{type:String,default:""}},slots:Object,setup(e,{slots:t}){const l=W(e.active),n=Ue([]),a=()=>{e.tabId&&(Pn.value[e.tabId]=e.data[l.value].id)},i=(u=l.value)=>{l.value=u{l.value=u>0?u-1:n.value.length-1,n.value[l.value].focus()},o=(u,d)=>{u.key===" "||u.key==="Enter"?(u.preventDefault(),l.value=d):u.key==="ArrowRight"?(u.preventDefault(),i()):u.key==="ArrowLeft"&&(u.preventDefault(),r()),e.tabId&&(Pn.value[e.tabId]=e.data[l.value].id)},c=()=>{if(e.tabId){const u=e.data.findIndex(({id:d})=>Pn.value[e.tabId]===d);if(u!==-1)return u}return e.active};return ke(()=>{l.value=c(),ce(()=>Pn.value[e.tabId],(u,d)=>{if(e.tabId&&u!==d){const p=e.data.findIndex(({id:h})=>h===u);p!==-1&&(l.value=p)}})}),()=>e.data.length?s("div",{class:"vp-code-tabs"},[s("div",{class:"vp-code-tabs-nav",role:"tablist"},e.data.map(({id:u},d)=>{const p=d===l.value;return s("button",{type:"button",ref:h=>{h&&(n.value[d]=h)},class:["vp-code-tab-nav",{active:p}],role:"tab","aria-controls":`codetab-${e.id}-${d}`,"aria-selected":p,onClick:()=>{l.value=d,a()},onKeydown:h=>o(h,d)},t[`title${d}`]({value:u,isActive:p}))})),e.data.map(({id:u},d)=>{const p=d===l.value;return s("div",{class:["vp-code-tab",{active:p}],id:`codetab-${e.id}-${d}`,role:"tabpanel","aria-expanded":p},t[`tab${d}`]({value:u,isActive:p}))})]):null}});const Ic=({active:e=!1},{slots:t})=>{var l;return s("div",{class:["code-group-item",{active:e}],"aria-selected":e},(l=t.default)==null?void 0:l.call(t))};Ic.displayName="CodeGroupItem";const Sf=M({name:"CodeGroup",slots:Object,setup(e,{slots:t}){const l=W(-1),n=Ue([]),a=(o=l.value)=>{l.value=o{l.value=o>0?o-1:n.value.length-1,n.value[l.value].focus()},r=(o,c)=>{o.key===" "||o.key==="Enter"?(o.preventDefault(),l.value=c):o.key==="ArrowRight"?(o.preventDefault(),a(c)):o.key==="ArrowLeft"&&(o.preventDefault(),i(c))};return()=>{var o;const c=(((o=t.default)==null?void 0:o.call(t))||[]).filter(u=>u.type.name==="CodeGroupItem").map(u=>(u.props===null&&(u.props={}),u));return c.length===0?null:(l.value<0||l.value>c.length-1?(l.value=c.findIndex(u=>"active"in u.props),l.value===-1&&(l.value=0)):c.forEach((u,d)=>{u.props.active=d===l.value}),s("div",{class:"code-group"},[s("div",{class:"code-group-nav"},c.map((u,d)=>{const p=d===l.value;return s("button",{type:"button",ref:h=>{h&&(n.value[d]=h)},class:["code-group-nav-tab",{active:p}],"aria-pressed":p,"aria-expanded":p,onClick:()=>{l.value=d},onKeydown:h=>r(h,d)},u.props.title)})),c]))}}});const La=Ri("VUEPRESS_TAB_STORE",{});var Df=M({name:"Tabs",props:{active:{type:Number,default:0},data:{type:Array,required:!0},id:{type:String,required:!0},tabId:{type:String,default:""}},slots:Object,setup(e,{slots:t}){const l=W(e.active),n=Ue([]),a=()=>{e.tabId&&(La.value[e.tabId]=e.data[l.value].id)},i=(u=l.value)=>{l.value=u{l.value=u>0?u-1:n.value.length-1,n.value[l.value].focus()},o=(u,d)=>{u.key===" "||u.key==="Enter"?(u.preventDefault(),l.value=d):u.key==="ArrowRight"?(u.preventDefault(),i()):u.key==="ArrowLeft"&&(u.preventDefault(),r()),a()},c=()=>{if(e.tabId){const u=e.data.findIndex(({id:d})=>La.value[e.tabId]===d);if(u!==-1)return u}return e.active};return ke(()=>{l.value=c(),ce(()=>La.value[e.tabId],(u,d)=>{if(e.tabId&&u!==d){const p=e.data.findIndex(({id:h})=>h===u);p!==-1&&(l.value=p)}})}),()=>e.data.length?s("div",{class:"vp-tabs"},[s("div",{class:"vp-tabs-nav",role:"tablist"},e.data.map(({id:u},d)=>{const p=d===l.value;return s("button",{type:"button",ref:h=>{h&&(n.value[d]=h)},class:["vp-tab-nav",{active:p}],role:"tab","aria-controls":`tab-${e.id}-${d}`,"aria-selected":p,onClick:()=>{l.value=d,a()},onKeydown:h=>o(h,d)},t[`title${d}`]({value:u,isActive:p}))})),e.data.map(({id:u},d)=>{const p=d===l.value;return s("div",{class:["vp-tab",{active:p}],id:`tab-${e.id}-${d}`,role:"tabpanel","aria-expanded":p},t[`tab${d}`]({value:u,isActive:p}))})]):null}});const $f=dt({enhance:({app:e})=>{e.component("CodeTabs",Rf),ct("CodeGroup",e)||e.component("CodeGroup",Sf),ct("CodeGroupItem",e)||e.component("CodeGroupItem",Ic),e.component("Tabs",Df)},setup:()=>{}});let Mf={};const Pc=Symbol(""),Vf=()=>me(Pc),Ff=e=>{e.provide(Pc,Mf)};const Nf=".theme-hope-content :not(a) > img:not([no-view])",jf={"/":{closeTitle:"关闭",downloadTitle:"下载图片",fullscreenTitle:"切换全屏",zoomTitle:"缩放",arrowPrevTitle:"上一个 (左箭头)",arrowNextTitle:"下一个 (右箭头)"}},Bf=800,zf='
',Hf=e=>ae(e)?Array.from(document.querySelectorAll(e)):e.map(t=>Array.from(document.querySelectorAll(t))).flat(),Oc=e=>new Promise((t,l)=>{e.complete?t({type:"image",element:e,src:e.src,width:e.naturalWidth,height:e.naturalHeight,alt:e.alt,msrc:e.src}):(e.onload=()=>t(Oc(e)),e.onerror=n=>l(n))}),qf=()=>{const{isSupported:e,toggle:t}=Si(),l=Vf(),n=on(jf),a=ue();let i;const r=c=>{c.on("uiRegister",()=>{e&&c.ui.registerElement({name:"fullscreen",order:7,isButton:!0,html:'',onClick:()=>{t()}}),c.ui.registerElement({name:"download",order:8,isButton:!0,tagName:"a",html:{isCustomSVG:!0,inner:'',outlineID:"pswp__icn-download"},onInit:(u,d)=>{u.setAttribute("download",""),u.setAttribute("target","_blank"),u.setAttribute("rel","noopener"),d.on("change",()=>{u.setAttribute("href",d.currSlide.data.src)})}}),c.ui.registerElement({name:"bulletsIndicator",className:"photo-swipe-bullets-indicator",appendTo:"wrapper",onInit:(u,d)=>{const p=[];let h=-1;for(let f=0;f{d.goTo(p.indexOf(w.target))},p.push(b),u.appendChild(b)}d.on("change",()=>{h>=0&&p[h].classList.remove("active"),p[d.currIndex].classList.add("active"),h=d.currIndex})}})})},o=()=>Promise.all([y(()=>import("./photoswipe.esm-5794cde2.js"),[]),Tl().then(()=>new Promise(c=>setTimeout(c,Bf)).then(()=>Hf(Nf)))]).then(([{default:c},u])=>{const d=u.map(p=>({html:zf,element:p,msrc:p.src}));u.forEach((p,h)=>{const f=()=>{i=new c({preloaderDelay:0,showHideAnimationType:"zoom",...n.value,...l,dataSource:d,index:h,closeOnVerticalDrag:!0,wheelToZoom:!1}),r(i),i.addFilter("thumbEl",()=>p),i.addFilter("placeholderSrc",()=>p.src),i.init()};p.style.cursor="zoom-in",p.addEventListener("click",()=>{f()}),p.addEventListener("keypress",({key:b})=>{b==="Enter"&&f()})}),u.forEach((p,h)=>{Oc(p).then(f=>{d.splice(h,1,f),i==null||i.refreshSlideContent(h)})})});ke(()=>{Me("wheel",()=>{i==null||i.close()}),o(),ce(()=>a.value.path,()=>o())})};var Uf=dt({enhance:({app:e})=>{Ff(e)},setup:()=>{qf()}});const Cc=()=>{const e=ue();return E(()=>e.value.readingTime??null)},Ua=typeof{"/":{word:"约 $word 字",less1Minute:"小于 1 分钟",time:"大约 $time 分钟"}}>"u"?null:{"/":{word:"约 $word 字",less1Minute:"小于 1 分钟",time:"大约 $time 分钟"}},Rc=(e,t)=>{const{minutes:l,words:n}=e,{less1Minute:a,word:i,time:r}=t;return{time:l<1?a:r.replace("$time",Math.round(l).toString()),words:i.replace("$word",n.toString())}},ys={words:"",time:""},Sc=()=>Ua?on(Ua):E(()=>null),Gf=()=>{if(typeof Ua>"u")return E(()=>ys);const e=Cc(),t=Sc();return E(()=>e.value&&t.value?Rc(e.value,t.value):ys)},el=()=>Tc(),ie=()=>wf(),Il=()=>E(()=>!!el().value.pure);var Ta=M({name:"EmptyComponent",setup:()=>()=>null});const Wf="719px",Kf="1440px",Jf="false",$i={mobileBreakPoint:Wf,pcBreakPoint:Kf,enableThemeColor:Jf},Mi={"/daily/":["copy-code-may-not-be-guilty","you-dont-need-to-add-tenant_id-to-every-table","reflections-on-a-speech-by-cto-of-microsoft-china","iteration-retrospective-of-sanyuan","leverage-ai-to-boost-coding-productivity","claude-ai-in-action-extract-info-from-html","beyond-utf8-do-you-know-utf8mb4-and-collation","use-claude2-instead-of-chatgpt","vim-creator-pass-away","dont-try-to-argue-with-a-sb","what-is-the-difference-between-sh-and-bash","about-arm-things-you-need-to-know","a-vuepress2-entertaining-video","a-warning-from-navicat","things-I-have-to-vent-about-vue"]},Dc=e=>{const{icon:t="",color:l,size:n}=e,a={};return l&&(a.color=l),n&&(a.height=Number.isNaN(Number(n))?n:`${n}px`),Zt(t)?s("img",{class:"icon",src:t,"no-view":"",style:a}):na(t)?s("img",{class:"icon",src:Le(t),"no-view":"",style:a}):s(Je("FontIcon"),e)};Dc.displayName="HopeIcon";var Ne=Dc,be=(e=>(e.type="y",e.title="t",e.shortTitle="s",e.icon="i",e.author="a",e.date="d",e.localizedDate="l",e.category="c",e.tag="g",e.isEncrypted="n",e.isOriginal="o",e.readingTime="r",e.excerpt="e",e.sticky="u",e.cover="v",e.index="I",e.order="O",e))(be||{}),$c=(e=>(e.article="a",e.home="h",e.slide="s",e.page="p",e))($c||{});const pl=(e,t,l=!1)=>{const n=encodeURI(t);let a=_l(e,tc(n));a.name==="404"&&(a=_l(e,n));const{fullPath:i,meta:r,name:o}=a;return{text:!l&&r[be.shortTitle]?r[be.shortTitle]:r[be.title]||t,link:o==="404"?t:i,...r[be.icon]?{icon:r[be.icon]}:{}}},un=()=>{const e=Ve(),t=yt();return l=>{if(l)if(na(l))t.path!==l&&e.push(l);else if(Zt(l)||Io(l))window&&window.open(l);else{const n=t.path.slice(0,t.path.lastIndexOf("/"));e.push(`${n}/${encodeURI(l)}`)}}},Mc=()=>{const e=ie(),t=Ee();return E(()=>{const{author:l}=t.value;return l?Yl(l):l===!1?[]:Yl(e.value.author,!1)})},Qf=()=>{const e=Ee();return E(()=>nc(e.value.category).map(t=>{var l,n;return{name:t,path:((n=(l=me(Symbol.for("categoryMap")))==null?void 0:l.value.map[t])==null?void 0:n.path)||""}}))},Yf=()=>{const e=Ee();return E(()=>ac(e.value.tag).map(t=>{var l,n;return{name:t,path:((n=(l=me(Symbol.for("tagMap")))==null?void 0:l.value.map[t])==null?void 0:n.path)||""}}))},Xf=()=>{const e=Ee(),t=ue();return E(()=>{const l=Pi(e.value.date);if(l)return l;const{createdTime:n}=t.value.git||{};return n?new Date(n):null})},Zf=()=>{const e=ie(),t=ue(),l=Ee(),n=Mc(),a=Qf(),i=Yf(),r=Xf(),o=Cc(),c=Gf(),u=E(()=>({author:n.value,category:a.value,date:r.value,localizedDate:t.value.localizedDate,tag:i.value,isOriginal:l.value.isOriginal||!1,readingTime:o.value,readingTimeLocale:c.value,pageview:"pageview"in l.value?l.value.pageview:!0})),d=E(()=>"pageInfo"in l.value?l.value.pageInfo:"pageInfo"in e.value?e.value.pageInfo:null);return{info:u,items:d}},{mobileBreakPoint:ev,pcBreakPoint:tv}=$i,bs=e=>e.endsWith("px")?Number(e.slice(0,-2)):null,dn=()=>{const e=W(!1),t=W(!1),l=()=>{e.value=window.innerWidth<=(bs(ev)??719),t.value=window.innerWidth>=(bs(tv)??1440)};return ke(()=>{l(),Me("resize",l,!1),Me("orientationchange",l,!1)}),{isMobile:e,isPC:t}},Vc=Symbol(""),pn=()=>{const e=me(Vc);if(!e)throw new Error("useDarkmode() is called without provider.");return e},lv=e=>{const t=el(),l=Nh(),n=Ri("vuepress-theme-hope-scheme","auto"),a=E(()=>t.value.darkmode||"switch"),i=E(()=>{const o=a.value;return o==="disable"?!1:o==="enable"?!0:o==="auto"?l.value:o==="toggle"?n.value==="dark":n.value==="dark"||n.value==="auto"&&l.value}),r=E(()=>{const o=a.value;return o==="switch"||o==="toggle"});e.provide(Vc,{canToggle:r,config:a,isDarkmode:i,status:n}),Object.defineProperties(e.config.globalProperties,{$isDarkmode:{get:()=>i.value}})},nv=()=>{const{isDarkmode:e}=pn(),t=(l=e.value)=>document.documentElement.setAttribute("data-theme",l?"dark":"light");ke(()=>{ce(e,t,{immediate:!0})})};var He=M({name:"AutoLink",inheritAttrs:!1,props:{config:{type:Object,required:!0},exact:Boolean,noExternalLinkIcon:Boolean},emits:["focusout"],slots:Object,setup(e,{attrs:t,emit:l,slots:n}){const a=yt(),i=Vo(),r=Ll(e,"config"),o=E(()=>Zt(r.value.link)),c=E(()=>Io(r.value.link)||P0(r.value.link)),u=E(()=>c.value?void 0:r.value.target||(o.value?"_blank":void 0)),d=E(()=>u.value==="_blank"),p=E(()=>!o.value&&!c.value&&!d.value),h=E(()=>c.value?void 0:r.value.rel||(d.value?"noopener noreferrer":void 0)),f=E(()=>r.value.ariaLabel||r.value.text),b=E(()=>{if(e.exact)return!1;const x=gt(i.value.locales);return x.length?x.every(g=>g!==r.value.link):r.value.link!=="/"}),w=E(()=>p.value?r.value.activeMatch?new RegExp(r.value.activeMatch).test(a.path):b.value?Ql(a.path,r.value.link):a.path===r.value.link:!1);return()=>{const{before:x,after:g,default:_}=n,{text:P,icon:O,link:B}=r.value;return p.value?s(Re,{to:B,"aria-label":f.value,...t,class:["nav-link",{active:w.value},t.class],onFocusout:()=>l("focusout")},()=>_?_():[x?x():s(Ne,{icon:O}),P,g==null?void 0:g()]):s("a",{href:B,rel:h.value,target:u.value,"aria-label":f.value,...t,class:["nav-link",t.class],onFocusout:()=>l("focusout")},_?_():[x?x():s(Ne,{icon:O}),P,e.noExternalLinkIcon?null:s(xc),g==null?void 0:g()])}}});const kl=(e,t,l=!1)=>"activeMatch"in t?new RegExp(t.activeMatch).test(e.path):Ci(e,t.link)?!0:t.children&&!l?t.children.some(n=>kl(e,n)):!1,Fc=(e,t)=>t.type==="group"?t.children.some(l=>l.type==="group"?Fc(e,l):l.type==="page"&&kl(e,l,!0))||"prefix"in t&&Ci(e,t.prefix):!1,Nc=(e,t)=>ae(e.link)?s(He,{...t,config:e}):s("p",t,[s(Ne,{icon:e.icon}),e.text]),jc=e=>{const t=yt();return e?s("ul",{class:"vp-sidebar-sub-headers"},e.map(l=>{const n=kl(t,l,!0);return s("li",{class:"vp-sidebar-sub-header"},[Nc(l,{class:["vp-sidebar-link","vp-heading",{active:n}]}),jc(l.children)])})):null},Aa=(e="",t="")=>na(t)?t:`${T0(e)}${t}`,av=(e,t)=>{const l=ue();return{type:"heading",text:e.title,link:`${l.value.path}#${e.slug}`,children:Vi(e.children,t)}},Vi=(e,t)=>t>0?e.map(l=>av(l,t-1)):[],Bc=e=>{const t=ue();return Vi(t.value.headers,e)},Ga=(e,t,l="")=>{const n=Ve(),a=ue(),i=(r,o=l)=>{var c;const u=ae(r)?pl(n,Aa(o,r)):r.link?{...r,...Bn(r.link)?{}:{link:pl(n,Aa(o,r.link)).link}}:r;if("children"in u){const d=Aa(o,u.prefix),p=u.children==="structure"?Mi[d]:u.children;return{type:"group",...u,prefix:d,children:p.map(h=>i(h,d))}}return{type:"page",...u,children:u.link===a.value.path?Vi(((c=a.value.headers[0])==null?void 0:c.level)===1?a.value.headers[0].children:a.value.headers,t):[]}};return e.map(r=>i(r))},iv=(e,t)=>{const l=ue(),n=gt(e).sort((a,i)=>i.length-a.length);for(const a of n)if(Ql(decodeURI(l.value.path),a)){const i=e[a];return i?Ga(i==="structure"?Mi[a]:i==="heading"?Bc(t):i,t,a):[]}return console.warn(`${l.value.path} is missing sidebar config.`),[]},rv=(e,t)=>{const l=mt();return e===!1?[]:e==="heading"?Bc(t):e==="structure"?Ga(Mi[l.value],t,l.value):X(e)?Ga(e,t):xi(e)?iv(e,t):[]},zc=Symbol(""),sv=()=>{const e=Ee(),t=ie(),l=E(()=>e.value.home?!1:e.value.sidebar??t.value.sidebar??"structure"),n=E(()=>e.value.headerDepth??t.value.headerDepth??2),a=E(()=>rv(l.value,n.value));ot(zc,a)},Fi=()=>{const e=me(zc);if(!e)throw new Error("useSidebarItems() is called without provider.");return e};var ov=M({name:"PageFooter",setup(){const e=Ee(),t=ie(),l=Mc(),n=E(()=>{const{copyright:r,footer:o}=e.value;return o!==!1&&!!(r||o||t.value.displayFooter)}),a=E(()=>{const{footer:r}=e.value;return r===!1?!1:ae(r)?r:t.value.footer||""}),i=E(()=>"copyright"in e.value?e.value.copyright:"copyright"in t.value?t.value.copyright:l.value.length?`Copyright © ${new Date().getFullYear()} ${l.value[0].name}`:!1);return()=>n.value?s("footer",{class:"vp-footer-wrapper"},[a.value?s("div",{class:"vp-footer",innerHTML:a.value}):null,i.value?s("div",{class:"vp-copyright",innerHTML:i.value}):null]):null}}),cv=M({name:"NavbarDropdownLink",props:{config:{type:Object,required:!0}},slots:Object,setup(e,{slots:t}){const l=ue(),n=Ll(e,"config"),a=E(()=>n.value.ariaLabel||n.value.text),i=W(!1);ce(()=>l.value.path,()=>{i.value=!1});const r=o=>{o.detail===0&&(i.value=!i.value)};return()=>{var o;return s("div",{class:["dropdown-wrapper",{open:i.value}]},[s("button",{type:"button",class:"dropdown-title","aria-label":a.value,onClick:r},[((o=t.title)==null?void 0:o.call(t))||s("span",{class:"title"},[s(Ne,{icon:n.value.icon}),e.config.text]),s("span",{class:"arrow"}),s("ul",{class:"nav-dropdown"},n.value.children.map((c,u)=>{const d=u===n.value.children.length-1;return s("li",{class:"dropdown-item"},"children"in c?[s("h4",{class:"dropdown-subtitle"},c.link?s(He,{config:c,onFocusout:()=>{c.children.length===0&&d&&(i.value=!1)}}):s("span",c.text)),s("ul",{class:"dropdown-subitem-wrapper"},c.children.map((p,h)=>s("li",{class:"dropdown-subitem"},s(He,{config:p,onFocusout:()=>{h===c.children.length-1&&d&&(i.value=!1)}}))))]:s(He,{config:c,onFocusout:()=>{d&&(i.value=!1)}}))}))])])}}});const Hc=(e,t,l="")=>ae(t)?pl(e,`${l}${t}`):"children"in t?{...t,...t.link&&!Bn(t.link)?pl(e,`${l}${t.link}`):{},children:t.children.map(n=>Hc(e,n,`${l}${t.prefix||""}`))}:{...t,link:Bn(t.link)?t.link:pl(e,`${l}${t.link}`).link},qc=()=>{const e=ie(),t=Ve(),l=()=>(e.value.navbar||[]).map(a=>Hc(t,a)),n=W(l());return ce(e,()=>{n.value=l()}),n},uv=()=>{const e=ie(),t=E(()=>e.value.repo||null),l=E(()=>t.value?Z1(t.value):null),n=E(()=>t.value?rc(t.value):null),a=E(()=>l.value?e.value.repoLabel??(n.value===null?"Source":n.value):null);return E(()=>!l.value||!a.value||e.value.repoDisplay===!1?null:{type:n.value||"Source",label:a.value,link:l.value})};var dv=M({name:"NavScreenDropdown",props:{config:{type:Object,required:!0}},setup(e){const t=ue(),l=Ll(e,"config"),n=E(()=>l.value.ariaLabel||l.value.text),a=W(!1);ce(()=>t.value.path,()=>{a.value=!1});const i=(r,o)=>o[o.length-1]===r;return()=>[s("button",{type:"button",class:["nav-screen-dropdown-title",{active:a.value}],"aria-label":n.value,onClick:()=>{a.value=!a.value}},[s("span",{class:"title"},[s(Ne,{icon:l.value.icon}),e.config.text]),s("span",{class:["arrow",a.value?"down":"end"]})]),s("ul",{class:["nav-screen-dropdown",{hide:!a.value}]},l.value.children.map(r=>s("li",{class:"dropdown-item"},"children"in r?[s("h4",{class:"dropdown-subtitle"},r.link?s(He,{config:r,onFocusout:()=>{i(r,l.value.children)&&r.children.length===0&&(a.value=!1)}}):s("span",r.text)),s("ul",{class:"dropdown-subitem-wrapper"},r.children.map(o=>s("li",{class:"dropdown-subitem"},s(He,{config:o,onFocusout:()=>{i(o,r.children)&&i(r,l.value.children)&&(a.value=!1)}}))))]:s(He,{config:r,onFocusout:()=>{i(r,l.value.children)&&(a.value=!1)}}))))]}}),pv=M({name:"NavScreenLinks",setup(){const e=qc();return()=>e.value.length?s("nav",{class:"nav-screen-links"},e.value.map(t=>s("div",{class:"navbar-links-item"},"children"in t?s(dv,{config:t}):s(He,{config:t})))):null}});const Uc=()=>s(de,{name:"dark"},()=>s("path",{d:"M524.8 938.667h-4.267a439.893 439.893 0 0 1-313.173-134.4 446.293 446.293 0 0 1-11.093-597.334A432.213 432.213 0 0 1 366.933 90.027a42.667 42.667 0 0 1 45.227 9.386 42.667 42.667 0 0 1 10.24 42.667 358.4 358.4 0 0 0 82.773 375.893 361.387 361.387 0 0 0 376.747 82.774 42.667 42.667 0 0 1 54.187 55.04 433.493 433.493 0 0 1-99.84 154.88 438.613 438.613 0 0 1-311.467 128z"}));Uc.displayName="DarkIcon";const Gc=()=>s(de,{name:"light"},()=>s("path",{d:"M952 552h-80a40 40 0 0 1 0-80h80a40 40 0 0 1 0 80zM801.88 280.08a41 41 0 0 1-57.96-57.96l57.96-58a41.04 41.04 0 0 1 58 58l-58 57.96zM512 752a240 240 0 1 1 0-480 240 240 0 0 1 0 480zm0-560a40 40 0 0 1-40-40V72a40 40 0 0 1 80 0v80a40 40 0 0 1-40 40zm-289.88 88.08-58-57.96a41.04 41.04 0 0 1 58-58l57.96 58a41 41 0 0 1-57.96 57.96zM192 512a40 40 0 0 1-40 40H72a40 40 0 0 1 0-80h80a40 40 0 0 1 40 40zm30.12 231.92a41 41 0 0 1 57.96 57.96l-57.96 58a41.04 41.04 0 0 1-58-58l58-57.96zM512 832a40 40 0 0 1 40 40v80a40 40 0 0 1-80 0v-80a40 40 0 0 1 40-40zm289.88-88.08 58 57.96a41.04 41.04 0 0 1-58 58l-57.96-58a41 41 0 0 1 57.96-57.96z"}));Gc.displayName="LightIcon";const Wc=()=>s(de,{name:"auto"},()=>s("path",{d:"M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm0-840c-198.78 0-360 161.22-360 360 0 198.84 161.22 360 360 360s360-161.16 360-360c0-198.78-161.22-360-360-360zm0 660V212c165.72 0 300 134.34 300 300 0 165.72-134.28 300-300 300z"}));Wc.displayName="AutoIcon";const Kc=()=>s(de,{name:"enter-fullscreen"},()=>s("path",{d:"M762.773 90.24h-497.28c-96.106 0-174.4 78.293-174.4 174.4v497.28c0 96.107 78.294 174.4 174.4 174.4h497.28c96.107 0 175.04-78.293 174.4-174.4V264.64c0-96.213-78.186-174.4-174.4-174.4zm-387.2 761.173H215.04c-21.867 0-40.427-17.92-41.067-41.066V649.92c0-22.507 17.92-40.427 40.427-40.427 11.307 0 21.227 4.694 28.48 11.947 7.253 7.253 11.947 17.92 11.947 28.48v62.293l145.28-145.28c15.893-15.893 41.813-15.893 57.706 0 15.894 15.894 15.894 41.814 0 57.707l-145.28 145.28h62.294c22.506 0 40.426 17.92 40.426 40.427s-17.173 41.066-39.68 41.066zM650.24 165.76h160.427c21.866 0 40.426 17.92 41.066 41.067v160.426c0 22.507-17.92 40.427-40.426 40.427-11.307 0-21.227-4.693-28.48-11.947-7.254-7.253-11.947-17.92-11.947-28.48v-62.186L625.6 450.347c-15.893 15.893-41.813 15.893-57.707 0-15.893-15.894-15.893-41.814 0-57.707l145.28-145.28H650.88c-22.507 0-40.427-17.92-40.427-40.427s17.174-41.173 39.787-41.173z"}));Kc.displayName="EnterFullScreenIcon";const Jc=()=>s(de,{name:"cancel-fullscreen"},()=>s("path",{d:"M778.468 78.62H247.922c-102.514 0-186.027 83.513-186.027 186.027V795.08c0 102.514 83.513 186.027 186.027 186.027h530.432c102.514 0 186.71-83.513 186.026-186.027V264.647C964.494 162.02 880.981 78.62 778.468 78.62zM250.88 574.35h171.122c23.324 0 43.122 19.115 43.804 43.805v171.121c0 24.008-19.114 43.122-43.122 43.122-12.06 0-22.641-5.006-30.378-12.743s-12.743-19.115-12.743-30.379V722.83L224.597 877.91c-16.953 16.952-44.6 16.952-61.553 0-16.953-16.954-16.953-44.602 0-61.554L318.009 661.39h-66.446c-24.007 0-43.122-19.114-43.122-43.122 0-24.12 18.432-43.918 42.439-43.918zm521.899-98.873H601.657c-23.325 0-43.122-19.114-43.805-43.804V260.55c0-24.007 19.115-43.122 43.122-43.122 12.06 0 22.642 5.007 30.379 12.743s12.743 19.115 12.743 30.38v66.445l154.965-154.965c16.953-16.953 44.601-16.953 61.554 0 16.953 16.953 16.953 44.6 0 61.554L705.536 388.55h66.446c24.007 0 43.122 19.115 43.122 43.122.114 24.007-18.318 43.804-42.325 43.804z"}));Jc.displayName="CancelFullScreenIcon";const Qc=()=>s(de,{name:"outlook"},()=>[s("path",{d:"M224 800c0 9.6 3.2 44.8 6.4 54.4 6.4 48-48 76.8-48 76.8s80 41.6 147.2 0 134.4-134.4 38.4-195.2c-22.4-12.8-41.6-19.2-57.6-19.2C259.2 716.8 227.2 761.6 224 800zM560 675.2l-32 51.2c-51.2 51.2-83.2 32-83.2 32 25.6 67.2 0 112-12.8 128 25.6 6.4 51.2 9.6 80 9.6 54.4 0 102.4-9.6 150.4-32l0 0c3.2 0 3.2-3.2 3.2-3.2 22.4-16 12.8-35.2 6.4-44.8-9.6-12.8-12.8-25.6-12.8-41.6 0-54.4 60.8-99.2 137.6-99.2 6.4 0 12.8 0 22.4 0 12.8 0 38.4 9.6 48-25.6 0-3.2 0-3.2 3.2-6.4 0-3.2 3.2-6.4 3.2-6.4 6.4-16 6.4-16 6.4-19.2 9.6-35.2 16-73.6 16-115.2 0-105.6-41.6-198.4-108.8-268.8C704 396.8 560 675.2 560 675.2zM224 419.2c0-28.8 22.4-51.2 51.2-51.2 28.8 0 51.2 22.4 51.2 51.2 0 28.8-22.4 51.2-51.2 51.2C246.4 470.4 224 448 224 419.2zM320 284.8c0-22.4 19.2-41.6 41.6-41.6 22.4 0 41.6 19.2 41.6 41.6 0 22.4-19.2 41.6-41.6 41.6C339.2 326.4 320 307.2 320 284.8zM457.6 208c0-12.8 12.8-25.6 25.6-25.6 12.8 0 25.6 12.8 25.6 25.6 0 12.8-12.8 25.6-25.6 25.6C470.4 233.6 457.6 220.8 457.6 208zM128 505.6C128 592 153.6 672 201.6 736c28.8-60.8 112-60.8 124.8-60.8-16-51.2 16-99.2 16-99.2l316.8-422.4c-48-19.2-99.2-32-150.4-32C297.6 118.4 128 291.2 128 505.6zM764.8 86.4c-22.4 19.2-390.4 518.4-390.4 518.4-22.4 28.8-12.8 76.8 22.4 99.2l9.6 6.4c35.2 22.4 80 12.8 99.2-25.6 0 0 6.4-12.8 9.6-19.2 54.4-105.6 275.2-524.8 288-553.6 6.4-19.2-3.2-32-19.2-32C777.6 76.8 771.2 80 764.8 86.4z"})]);Qc.displayName="OutlookIcon";var Yc=M({name:"AppearanceSwitch",setup(){const{config:e,status:t}=pn(),l=()=>{e.value==="switch"?t.value={light:"dark",dark:"auto",auto:"light"}[t.value]:t.value=t.value==="light"?"dark":"light"};return()=>s("button",{type:"button",id:"appearance-switch",onClick:()=>l()},[s(Wc,{style:{display:t.value==="auto"?"block":"none"}}),s(Uc,{style:{display:t.value==="dark"?"block":"none"}}),s(Gc,{style:{display:t.value==="light"?"block":"none"}})])}}),hv=M({name:"AppearanceMode",setup(){const e=ie(),{canToggle:t}=pn(),l=E(()=>e.value.outlookLocales.darkmode);return()=>t.value?s("div",{class:"appearance-wrapper"},[s("label",{class:"appearance-title",for:"appearance-switch"},l.value),s(Yc)]):null}});const Ia="VUEPRESS_THEME_COLOR";var fv=M({name:"ThemeColorPicker",props:{themeColor:{type:Object,required:!0}},setup(e){const t=(l="")=>{const n=document.documentElement.classList,a=gt(e.themeColor);if(!l){localStorage.removeItem(Ia),n.remove(...a);return}n.remove(...a.filter(i=>i!==l)),n.add(l),localStorage.setItem(Ia,l)};return ke(()=>{const l=localStorage.getItem(Ia);l&&t(l)}),()=>s("ul",{id:"theme-color-picker"},[s("li",s("span",{class:"theme-color",onClick:()=>t()})),sn(e.themeColor).map(([l,n])=>s("li",s("span",{style:{background:n},onClick:()=>t(l)})))])}});const hl=$i.enableThemeColor==="true",vv=hl?J1(sn($i).filter(([e])=>e.startsWith("theme-"))):{};var gv=M({name:"ThemeColor",setup(){const e=ie(),t=E(()=>e.value.outlookLocales.themeColor);return()=>hl?s("div",{class:"theme-color-wrapper"},[s("label",{class:"theme-color-title",for:"theme-color-picker"},t.value),s(fv,{themeColor:vv})]):null}}),Xc=M({name:"ToggleFullScreenButton",setup(){const e=ie(),{isSupported:t,isFullscreen:l,toggle:n}=Si(),a=E(()=>e.value.outlookLocales.fullscreen);return()=>t?s("div",{class:"full-screen-wrapper"},[s("label",{class:"full-screen-title",for:"full-screen-switch"},a.value),s("button",{type:"button",id:"full-screen-switch",class:"full-screen",ariaPressed:l.value,onClick:()=>n()},l.value?s(Jc):s(Kc))]):null}}),Zc=M({name:"OutlookSettings",setup(){const e=el(),t=Il(),l=E(()=>!t.value&&e.value.fullscreen);return()=>s(Zn,()=>[hl?s(gv):null,s(hv),l.value?s(Xc):null])}}),mv=M({name:"NavScreen",props:{show:Boolean},emits:["close"],slots:Object,setup(e,{emit:t,slots:l}){const n=ue(),{isMobile:a}=dn(),i=Ue(),r=_c(i);return ke(()=>{i.value=document.body,ce(a,o=>{!o&&e.show&&(r.value=!1,t("close"))}),ce(()=>n.value.path,()=>{r.value=!1,t("close")})}),an(()=>{r.value=!1}),()=>s(Bt,{name:"fade",onEnter:()=>{r.value=!0},onAfterLeave:()=>{r.value=!1}},()=>{var o,c;return e.show?s("div",{id:"nav-screen"},s("div",{class:"vp-nav-screen-container"},[(o=l.before)==null?void 0:o.call(l),s(pv),s("div",{class:"vp-outlook-wrapper"},s(Zc)),(c=l.after)==null?void 0:c.call(l)])):null})}}),yv=M({name:"NavbarBrand",setup(){const e=mt(),t=rn(),l=ie(),n=E(()=>l.value.home||e.value),a=E(()=>t.value.title),i=E(()=>l.value.navTitle??a.value),r=E(()=>l.value.logo?Le(l.value.logo):null),o=E(()=>l.value.logoDark?Le(l.value.logoDark):null);return()=>s(Re,{to:n.value,class:"vp-brand"},()=>[r.value?s("img",{class:["vp-nav-logo",{light:!!o.value}],src:r.value,alt:a.value}):null,o.value?s("img",{class:["vp-nav-logo dark"],src:o.value,alt:a.value}):null,i.value?s("span",{class:["vp-site-name",{"hide-in-pad":r.value&&l.value.hideSiteNameOnMobile!==!1}]},i.value):null])}}),bv=M({name:"NavbarLinks",setup(){const e=qc();return()=>e.value.length?s("nav",{class:"vp-nav-links"},e.value.map(t=>s("div",{class:"nav-item hide-in-mobile"},"children"in t?s(cv,{config:t}):s(He,{config:t})))):null}}),_v=M({name:"RepoLink",components:{BitbucketIcon:uc,GiteeIcon:cc,GitHubIcon:sc,GitLabIcon:oc,SourceIcon:dc},setup(){const e=uv();return()=>e.value?s("div",{class:"nav-item vp-repo"},s("a",{class:"vp-repo-link",href:e.value.link,target:"_blank",rel:"noopener noreferrer","aria-label":e.value.label},s(Je(`${e.value.type}Icon`),{style:{width:"1.25rem",height:"1.25rem",verticalAlign:"middle"}}))):null}});const eu=({active:e=!1},{emit:t})=>s("button",{type:"button",class:["vp-toggle-navbar-button",{"is-active":e}],"aria-label":"Toggle Navbar","aria-expanded":e,"aria-controls":"nav-screen",onClick:()=>t("toggle")},s("span",[s("span",{class:"vp-top"}),s("span",{class:"vp-middle"}),s("span",{class:"vp-bottom"})]));eu.displayName="ToggleNavbarButton";var kv=eu;const Wa=(e,{emit:t})=>s("button",{type:"button",class:"vp-toggle-sidebar-button",title:"Toggle Sidebar",onClick:()=>t("toggle")},s("span",{class:"icon"}));Wa.displayName="ToggleSidebarButton",Wa.emits=["toggle"];var wv=Wa,Ev=M({name:"OutlookButton",setup(){const{isSupported:e}=Si(),t=el(),l=Il(),n=ue(),{canToggle:a}=pn(),i=W(!1),r=E(()=>!l.value&&t.value.fullscreen&&e);return ce(()=>n.value.path,()=>{i.value=!1}),()=>a.value||r.value||hl?s("div",{class:"nav-item hide-in-mobile"},a.value&&!r.value&&!hl?s(Yc):r.value&&!a.value&&!hl?s(Xc):s("button",{type:"button",class:["outlook-button",{open:i.value}],tabindex:"-1","aria-hidden":!0},[s(Qc),s("div",{class:"outlook-dropdown"},s(Zc))])):null}}),xv=M({name:"NavBar",emits:["toggleSidebar"],slots:Object,setup(e,{emit:t,slots:l}){const n=ie(),{isMobile:a}=dn(),i=W(!1),r=E(()=>{const{navbarAutoHide:d="mobile"}=n.value;return d!=="none"&&(d==="always"||a.value)}),o=E(()=>n.value.navbarLayout||{start:["Brand"],center:["Links"],end:["Language","Repo","Outlook","Search"]}),c={Brand:yv,Language:Ta,Links:bv,Repo:_v,Outlook:Ev,Search:ct("Docsearch")?Je("Docsearch"):ct("SearchBox")?Je("SearchBox"):Ta},u=d=>c[d]??(ct(d)?Je(d):Ta);return()=>{var d,p,h,f,b,w;return[s("header",{id:"navbar",class:["vp-navbar",{"auto-hide":r.value,"hide-icon":n.value.navbarIcon===!1}]},[s("div",{class:"vp-navbar-start"},[s(wv,{onToggle:()=>{i.value&&(i.value=!1),t("toggleSidebar")}}),(d=l.startBefore)==null?void 0:d.call(l),(o.value.start||[]).map(x=>s(u(x))),(p=l.startAfter)==null?void 0:p.call(l)]),s("div",{class:"vp-navbar-center"},[(h=l.centerBefore)==null?void 0:h.call(l),(o.value.center||[]).map(x=>s(u(x))),(f=l.centerAfter)==null?void 0:f.call(l)]),s("div",{class:"vp-navbar-end"},[(b=l.endBefore)==null?void 0:b.call(l),(o.value.end||[]).map(x=>s(u(x))),(w=l.endAfter)==null?void 0:w.call(l),s(kv,{active:i.value,onToggle:()=>{i.value=!i.value}})])]),s(mv,{show:i.value,onClose:()=>{i.value=!1}},{before:()=>{var x;return(x=l.screenTop)==null?void 0:x.call(l)},after:()=>{var x;return(x=l.screenBottom)==null?void 0:x.call(l)}})]}}}),Lv=M({name:"SidebarChild",props:{config:{type:Object,required:!0}},setup(e){const t=yt();return()=>[Nc(e.config,{class:["vp-sidebar-link",`vp-sidebar-${e.config.type}`,{active:kl(t,e.config,!0)}],exact:!0}),jc(e.config.children)]}}),Tv=M({name:"SidebarGroup",props:{config:{type:Object,required:!0},open:{type:Boolean,required:!0}},emits:["toggle"],setup(e,{emit:t}){const l=yt(),n=E(()=>kl(l,e.config)),a=E(()=>kl(l,e.config,!0));return()=>{const{collapsible:i,children:r=[],icon:o,prefix:c,link:u,text:d}=e.config;return s("section",{class:"vp-sidebar-group"},[s(i?"button":"p",{class:["vp-sidebar-heading",{clickable:i||u,exact:a.value,active:n.value}],...i?{type:"button",onClick:()=>t("toggle"),onKeydown:p=>{p.key==="Enter"&&t("toggle")}}:{}},[s(Ne,{icon:o}),u?s(He,{class:"vp-sidebar-title",config:{text:d,link:u},noExternalLinkIcon:!0}):s("span",{class:"vp-sidebar-title"},d),i?s("span",{class:["vp-arrow",e.open?"down":"end"]}):null]),e.open||!i?s(tu,{key:c,config:r}):null])}}}),tu=M({name:"SidebarLinks",props:{config:{type:Array,required:!0}},setup(e){const t=yt(),l=W(-1),n=a=>{l.value=a===l.value?-1:a};return ce(()=>t.path,()=>{const a=e.config.findIndex(i=>Fc(t,i));l.value=a},{immediate:!0,flush:"post"}),()=>s("ul",{class:"vp-sidebar-links"},e.config.map((a,i)=>s("li",a.type==="group"?s(Tv,{config:a,open:i===l.value,onToggle:()=>n(i)}):s(Lv,{config:a}))))}}),Av=M({name:"SideBar",slots:Object,setup(e,{slots:t}){const l=yt(),n=ie(),a=Fi(),i=Ue();return ke(()=>{ce(()=>l.hash,r=>{const o=document.querySelector(`.vp-sidebar a.vp-sidebar-link[href="${l.path}${r}"]`);if(!o)return;const{top:c,height:u}=i.value.getBoundingClientRect(),{top:d,height:p}=o.getBoundingClientRect();dc+u&&o.scrollIntoView(!1)})}),()=>{var r,o,c;return s("aside",{ref:i,id:"sidebar",class:["vp-sidebar",{"hide-icon":n.value.sidebarIcon===!1}]},[(r=t.top)==null?void 0:r.call(t),((o=t.default)==null?void 0:o.call(t))||s(tu,{config:a.value}),(c=t.bottom)==null?void 0:c.call(t)])}}}),Ni=M({name:"CommonWrapper",props:{containerClass:{type:String,default:""},noNavbar:Boolean,noSidebar:Boolean,noToc:Boolean},slots:Object,setup(e,{slots:t}){const l=Ve(),n=ue(),a=Ee(),i=ie(),{isMobile:r,isPC:o}=dn(),[c,u]=ls(!1),[d,p]=ls(!1),h=Fi(),f=W(!1),b=E(()=>e.noNavbar||a.value.navbar===!1||i.value.navbar===!1?!1:!!(n.value.title||i.value.logo||i.value.repo||i.value.navbar)),w=E(()=>e.noSidebar?!1:a.value.sidebar!==!1&&h.value.length!==0&&!a.value.home),x=E(()=>e.noToc||a.value.home?!1:a.value.toc||i.value.toc!==!1&&a.value.toc!==!1),g={x:0,y:0},_=C=>{g.x=C.changedTouches[0].clientX,g.y=C.changedTouches[0].clientY},P=C=>{const V=C.changedTouches[0].clientX-g.x,T=C.changedTouches[0].clientY-g.y;Math.abs(V)>Math.abs(T)*1.5&&Math.abs(V)>40&&(V>0&&g.x<=80?u(!0):u(!1))},O=()=>window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;let B=0;return Me("scroll",fh(()=>{const C=O();C<=58||C{C||u(!1)}),ke(()=>{const C=_c(document.body);ce(c,T=>{C.value=T});const V=l.afterEach(()=>{u(!1)});an(()=>{C.value=!1,V()})}),()=>s(ct("GlobalEncrypt")?Je("GlobalEncrypt"):Xo,()=>s("div",{class:["theme-container",{"no-navbar":!b.value,"no-sidebar":!w.value&&!(t.sidebar||t.sidebarTop||t.sidebarBottom),"has-toc":x.value,"hide-navbar":f.value,"sidebar-collapsed":!r.value&&!o.value&&d.value,"sidebar-open":r.value&&c.value},e.containerClass,a.value.containerClass||""],onTouchStart:_,onTouchEnd:P},[b.value?s(xv,{onToggleSidebar:()=>u()},{startBefore:()=>{var C;return(C=t.navbarStartBefore)==null?void 0:C.call(t)},startAfter:()=>{var C;return(C=t.navbarStartAfter)==null?void 0:C.call(t)},centerBefore:()=>{var C;return(C=t.navbarCenterBefore)==null?void 0:C.call(t)},centerAfter:()=>{var C;return(C=t.navbarCenterAfter)==null?void 0:C.call(t)},endBefore:()=>{var C;return(C=t.navbarEndBefore)==null?void 0:C.call(t)},endAfter:()=>{var C;return(C=t.navbarEndAfter)==null?void 0:C.call(t)},screenTop:()=>{var C;return(C=t.navScreenTop)==null?void 0:C.call(t)},screenBottom:()=>{var C;return(C=t.navScreenBottom)==null?void 0:C.call(t)}}):null,s(Bt,{name:"fade"},()=>c.value?s("div",{class:"vp-sidebar-mask",onClick:()=>u(!1)}):null),s(Bt,{name:"fade"},()=>r.value?null:s("div",{class:"toggle-sidebar-wrapper",onClick:()=>p()},s("span",{class:["arrow",d.value?"end":"start"]}))),s(Av,{},{...t.sidebar?{default:()=>t.sidebar()}:{},top:()=>{var C;return(C=t.sidebarTop)==null?void 0:C.call(t)},bottom:()=>{var C;return(C=t.sidebarBottom)==null?void 0:C.call(t)}}),t.default(),s(ov)]))}}),pe=M({name:"DropTransition",props:{type:{type:String,default:"single"},delay:{type:Number,default:0},duration:{type:Number,default:.25},appear:Boolean},slots:Object,setup(e,{slots:t}){const l=a=>{a.style.transition=`transform ${e.duration}s ease-in-out ${e.delay}s, opacity ${e.duration}s ease-in-out ${e.delay}s`,a.style.transform="translateY(-20px)",a.style.opacity="0"},n=a=>{a.style.transform="translateY(0)",a.style.opacity="1"};return()=>s(e.type==="single"?Bt:h0,{name:"drop",appear:e.appear,onAppear:l,onAfterAppear:n,onEnter:l,onAfterEnter:n,onBeforeLeave:l},()=>t.default())}});const Ka=({custom:e})=>s(No,{class:["theme-hope-content",{custom:e}]});Ka.displayName="MarkdownContent",Ka.props={custom:Boolean};var ji=Ka;const lu=()=>s(de,{name:"author"},()=>s("path",{d:"M649.6 633.6c86.4-48 147.2-144 147.2-249.6 0-160-128-288-288-288s-288 128-288 288c0 108.8 57.6 201.6 147.2 249.6-121.6 48-214.4 153.6-240 288-3.2 9.6 0 19.2 6.4 25.6 3.2 9.6 12.8 12.8 22.4 12.8h704c9.6 0 19.2-3.2 25.6-12.8 6.4-6.4 9.6-16 6.4-25.6-25.6-134.4-121.6-240-243.2-288z"}));lu.displayName="AuthorIcon";const nu=()=>s(de,{name:"calendar"},()=>s("path",{d:"M716.4 110.137c0-18.753-14.72-33.473-33.472-33.473-18.753 0-33.473 14.72-33.473 33.473v33.473h66.993v-33.473zm-334.87 0c0-18.753-14.72-33.473-33.473-33.473s-33.52 14.72-33.52 33.473v33.473h66.993v-33.473zm468.81 33.52H716.4v100.465c0 18.753-14.72 33.473-33.472 33.473a33.145 33.145 0 01-33.473-33.473V143.657H381.53v100.465c0 18.753-14.72 33.473-33.473 33.473a33.145 33.145 0 01-33.473-33.473V143.657H180.6A134.314 134.314 0 0046.66 277.595v535.756A134.314 134.314 0 00180.6 947.289h669.74a134.36 134.36 0 00133.94-133.938V277.595a134.314 134.314 0 00-133.94-133.938zm33.473 267.877H147.126a33.145 33.145 0 01-33.473-33.473c0-18.752 14.72-33.473 33.473-33.473h736.687c18.752 0 33.472 14.72 33.472 33.473a33.145 33.145 0 01-33.472 33.473z"}));nu.displayName="CalendarIcon";const au=()=>s(de,{name:"category"},()=>s("path",{d:"M148.41 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H148.41c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.311-40.31zM147.556 553.478H429.73c22.263 0 40.311 18.048 40.311 40.31v282.176c0 22.263-18.048 40.312-40.31 40.312H147.555c-22.263 0-40.311-18.049-40.311-40.312V593.79c0-22.263 18.048-40.311 40.31-40.311zM593.927 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H593.927c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.31-40.31zM730.22 920.502H623.926c-40.925 0-74.22-33.388-74.22-74.425V623.992c0-41.038 33.387-74.424 74.425-74.424h222.085c41.038 0 74.424 33.226 74.424 74.067v114.233c0 10.244-8.304 18.548-18.547 18.548s-18.548-8.304-18.548-18.548V623.635c0-20.388-16.746-36.974-37.33-36.974H624.13c-20.585 0-37.331 16.747-37.331 37.33v222.086c0 20.585 16.654 37.331 37.126 37.331H730.22c10.243 0 18.547 8.304 18.547 18.547 0 10.244-8.304 18.547-18.547 18.547z"}));au.displayName="CategoryIcon";const iu=()=>s(de,{name:"print"},()=>s("path",{d:"M819.2 364.8h-44.8V128c0-17.067-14.933-32-32-32H281.6c-17.067 0-32 14.933-32 32v236.8h-44.8C145.067 364.8 96 413.867 96 473.6v192c0 59.733 49.067 108.8 108.8 108.8h44.8V896c0 17.067 14.933 32 32 32h460.8c17.067 0 32-14.933 32-32V774.4h44.8c59.733 0 108.8-49.067 108.8-108.8v-192c0-59.733-49.067-108.8-108.8-108.8zM313.6 160h396.8v204.8H313.6V160zm396.8 704H313.6V620.8h396.8V864zM864 665.6c0 25.6-19.2 44.8-44.8 44.8h-44.8V588.8c0-17.067-14.933-32-32-32H281.6c-17.067 0-32 14.933-32 32v121.6h-44.8c-25.6 0-44.8-19.2-44.8-44.8v-192c0-25.6 19.2-44.8 44.8-44.8h614.4c25.6 0 44.8 19.2 44.8 44.8v192z"}));iu.displayName="PrintIcon";const ru=()=>s(de,{name:"tag"},()=>s("path",{d:"M939.902 458.563L910.17 144.567c-1.507-16.272-14.465-29.13-30.737-30.737L565.438 84.098h-.402c-3.215 0-5.726 1.005-7.634 2.913l-470.39 470.39a10.004 10.004 0 000 14.164l365.423 365.424c1.909 1.908 4.42 2.913 7.132 2.913s5.223-1.005 7.132-2.913l470.39-470.39c2.01-2.11 3.014-5.023 2.813-8.036zm-240.067-72.121c-35.458 0-64.286-28.828-64.286-64.286s28.828-64.285 64.286-64.285 64.286 28.828 64.286 64.285-28.829 64.286-64.286 64.286z"}));ru.displayName="TagIcon";const su=()=>s(de,{name:"timer"},()=>s("path",{d:"M799.387 122.15c4.402-2.978 7.38-7.897 7.38-13.463v-1.165c0-8.933-7.38-16.312-16.312-16.312H256.33c-8.933 0-16.311 7.38-16.311 16.312v1.165c0 5.825 2.977 10.874 7.637 13.592 4.143 194.44 97.22 354.963 220.201 392.763-122.204 37.542-214.893 196.511-220.2 389.397-4.661 5.049-7.638 11.651-7.638 19.03v5.825h566.49v-5.825c0-7.379-2.849-13.981-7.509-18.9-5.049-193.016-97.867-351.985-220.2-389.527 123.24-37.67 216.446-198.453 220.588-392.892zM531.16 450.445v352.632c117.674 1.553 211.787 40.778 211.787 88.676H304.097c0-48.286 95.149-87.382 213.728-88.676V450.445c-93.077-3.107-167.901-81.297-167.901-177.093 0-8.803 6.99-15.793 15.793-15.793 8.803 0 15.794 6.99 15.794 15.793 0 80.261 63.69 145.635 142.01 145.635s142.011-65.374 142.011-145.635c0-8.803 6.99-15.793 15.794-15.793s15.793 6.99 15.793 15.793c0 95.019-73.789 172.82-165.96 177.093z"}));su.displayName="TimerIcon";const ou=()=>s(de,{name:"word"},()=>[s("path",{d:"M518.217 432.64V73.143A73.143 73.143 0 01603.43 1.097a512 512 0 01419.474 419.474 73.143 73.143 0 01-72.046 85.212H591.36a73.143 73.143 0 01-73.143-73.143z"}),s("path",{d:"M493.714 566.857h340.297a73.143 73.143 0 0173.143 85.577A457.143 457.143 0 11371.566 117.76a73.143 73.143 0 0185.577 73.143v339.383a36.571 36.571 0 0036.571 36.571z"})]);ou.displayName="WordIcon";const Ht=()=>{const e=ie();return E(()=>e.value.metaLocales)};var Iv=M({name:"AuthorInfo",inheritAttrs:!1,props:{author:{type:Array,required:!0},pure:Boolean},setup(e){const t=Ht();return()=>e.author.length?s("span",{class:"page-author-info","aria-label":`${t.value.author}${e.pure?"":"🖊"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(lu),s("span",e.author.map(l=>l.url?s("a",{class:"page-author-item",href:l.url,target:"_blank",rel:"noopener noreferrer"},l.name):s("span",{class:"page-author-item"},l.name))),s("span",{property:"author",content:e.author.map(l=>l.name).join(", ")})]):null}}),Pv=M({name:"CategoryInfo",inheritAttrs:!1,props:{category:{type:Array,required:!0},pure:Boolean},setup(e){const t=Ve(),l=ue(),n=Ht(),a=(i,r="")=>{r&&l.value.path!==r&&(i.preventDefault(),t.push(r))};return()=>e.category.length?s("span",{class:"page-category-info","aria-label":`${n.value.category}${e.pure?"":"🌈"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(au),e.category.map(({name:i,path:r})=>s("span",{class:["page-category-item",{[`category${la(i,9)}`]:!e.pure,clickable:r}],role:r?"navigation":"",onClick:o=>a(o,r)},i)),s("meta",{property:"articleSection",content:e.category.map(({name:i})=>i).join(",")})]):null}}),Ov=M({name:"DateInfo",inheritAttrs:!1,props:{date:{type:Object,default:null},localizedDate:{type:String,default:""},pure:Boolean},setup(e){const t=$o(),l=Ht();return()=>e.date?s("span",{class:"page-date-info","aria-label":`${l.value.date}${e.pure?"":"📅"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(nu),s("span",s(Zn,()=>e.localizedDate||e.date.toLocaleDateString(t.value))),s("meta",{property:"datePublished",content:e.date.toISOString()||""})]):null}}),Cv=M({name:"OriginalInfo",inheritAttrs:!1,props:{isOriginal:Boolean},setup(e){const t=Ht();return()=>e.isOriginal?s("span",{class:"page-original-info"},t.value.origin):null}}),Rv=M({name:"ReadingTimeInfo",inheritAttrs:!1,props:{readingTime:{type:Object,default:()=>null},readingTimeLocale:{type:Object,default:()=>null},pure:Boolean},setup(e){const t=Ht(),l=E(()=>{if(!e.readingTime)return null;const{minutes:n}=e.readingTime;return n<1?"PT1M":`PT${Math.round(n)}M`});return()=>{var n,a;return(n=e.readingTimeLocale)!=null&&n.time?s("span",{class:"page-reading-time-info","aria-label":`${t.value.readingTime}${e.pure?"":"⌛"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(su),s("span",(a=e.readingTimeLocale)==null?void 0:a.time),s("meta",{property:"timeRequired",content:l.value})]):null}}}),Sv=M({name:"TagInfo",inheritAttrs:!1,props:{tag:{type:Array,default:()=>[]},pure:Boolean},setup(e){const t=Ve(),l=ue(),n=Ht(),a=(i,r="")=>{r&&l.value.path!==r&&(i.preventDefault(),t.push(r))};return()=>e.tag.length?s("span",{class:"page-tag-info","aria-label":`${n.value.tag}${e.pure?"":"🏷"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(ru),e.tag.map(({name:i,path:r})=>s("span",{class:["page-tag-item",{[`tag${la(i,9)}`]:!e.pure,clickable:r}],role:r?"navigation":"",onClick:o=>a(o,r)},i)),s("meta",{property:"keywords",content:e.tag.map(({name:i})=>i).join(",")})]):null}}),Dv=M({name:"ReadTimeInfo",inheritAttrs:!1,props:{readingTime:{type:Object,default:()=>null},readingTimeLocale:{type:Object,default:()=>null},pure:Boolean},setup(e){const t=Ht();return()=>{var l,n,a;return(l=e.readingTimeLocale)!=null&&l.words?s("span",{class:"page-word-info","aria-label":`${t.value.words}${e.pure?"":"🔠"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(ou),s("span",(n=e.readingTimeLocale)==null?void 0:n.words),s("meta",{property:"wordCount",content:(a=e.readingTime)==null?void 0:a.words})]):null}}}),cu=M({name:"PageInfo",components:{AuthorInfo:Iv,CategoryInfo:Pv,DateInfo:Ov,OriginalInfo:Cv,PageViewInfo:()=>null,ReadingTimeInfo:Rv,TagInfo:Sv,WordInfo:Dv},props:{items:{type:[Array,Boolean],default:()=>["Author","Original","Date","PageView","ReadingTime","Category","Tag"]},info:{type:Object,required:!0}},setup(e){const t=Il();return()=>e.items?s("div",{class:"page-info"},e.items.map(l=>s(Je(`${l}Info`),{...e.info,pure:t.value}))):null}}),$v=M({name:"PrintButton",setup(){const e=el(),t=ie();return()=>e.value.print===!1?null:s("button",{type:"button",class:"print-button",title:t.value.metaLocales.print,onClick:()=>{window.print()}},s(iu))}});const Mv=({title:e,level:t,slug:l})=>s(Re,{to:`#${l}`,class:["toc-link",`level${t}`]},()=>e),Ja=(e,t)=>{const l=yt();return e.length&&t>0?s("ul",{class:"toc-list"},e.map(n=>{const a=Ja(n.children,t-1);return[s("li",{class:["toc-item",{active:Ci(l,`#${n.slug}`)}]},Mv(n)),a?s("li",a):null]})):null};var uu=M({name:"TOC",props:{items:{type:Array,default:()=>[]},headerDepth:{type:Number,default:2}},slots:Object,setup(e,{slots:t}){const l=yt(),n=ue(),a=Ht(),i=Ue(),r=W("-1.7rem"),o=u=>{var d;(d=i.value)==null||d.scrollTo({top:u,behavior:"smooth"})},c=()=>{if(i.value){const u=document.querySelector(".toc-item.active");u?r.value=`${u.getBoundingClientRect().top-i.value.getBoundingClientRect().top+i.value.scrollTop}px`:r.value="-1.7rem"}else r.value="-1.7rem"};return ke(()=>{ce(()=>l.hash,u=>{if(i.value){const d=document.querySelector(`#toc a.toc-link[href$="${u}"]`);if(!d)return;const{top:p,height:h}=i.value.getBoundingClientRect(),{top:f,height:b}=d.getBoundingClientRect();fp+h&&o(i.value.scrollTop+f+b-p-h)}}),ce(()=>l.fullPath,()=>c(),{flush:"post",immediate:!0})}),()=>{var u,d;const p=e.items.length?Ja(e.items,e.headerDepth):n.value.headers?Ja(n.value.headers,e.headerDepth):null;return p?s("div",{class:"toc-place-holder"},[s("aside",{id:"toc"},[(u=t.before)==null?void 0:u.call(t),s("div",{class:"toc-header"},[a.value.toc,s($v)]),s("div",{class:"toc-wrapper",ref:i},[p,s("div",{class:"toc-marker",style:{top:r.value}})]),(d=t.after)==null?void 0:d.call(t)])]):null}}}),Bi=M({name:"SkipLink",props:{content:{type:String,default:"main-content"}},setup(e){const t=ue(),l=ie(),n=Ue(),a=({target:i})=>{const r=document.querySelector(i.hash);if(r){const o=()=>{r.removeAttribute("tabindex"),r.removeEventListener("blur",o)};r.setAttribute("tabindex","-1"),r.addEventListener("blur",o),r.focus(),window.scrollTo(0,0)}};return ke(()=>{ce(()=>t.value.path,()=>n.value.focus())}),()=>[s("span",{ref:n,tabindex:"-1"}),s("a",{href:`#${e.content}`,class:"vp-skip-link sr-only",onClick:a},l.value.routeLocales.skipToContent)]}});let Pa=null,On=null;const Vv={wait:()=>Pa,pending:()=>{Pa=new Promise(e=>On=e)},resolve:()=>{On==null||On(),Pa=null,On=null}},du=()=>Vv;var Fv=M({name:"FadeSlideY",slots:Object,setup(e,{slots:t}){const{resolve:l,pending:n}=du();return()=>s(Bt,{name:"fade-slide-y",mode:"out-in",onBeforeEnter:l,onBeforeLeave:n},()=>{var a;return(a=t.default)==null?void 0:a.call(t)})}});const Nv=(e,t)=>{const l=e.replace(t,"/").split("/"),n=[];let a=Li(t);return l.forEach((i,r)=>{r!==l.length-1?(a+=`${i}/`,n.push({link:a,name:i||"Home"})):i!==""&&(a+=i,n.push({link:a,name:i}))}),n},pu=(e,{slots:t})=>{var l,n;const{bgImage:a,bgImageDark:i,bgImageStyle:r,color:o,description:c,image:u,imageDark:d,header:p,features:h=[]}=e;return s("div",{class:"vp-feature-wrapper"},[a?s("div",{class:["vp-feature-bg",{light:i}],style:[{"background-image":`url(${a})`},r]}):null,i?s("div",{class:"vp-feature-bg dark",style:[{"background-image":`url(${i})`},r]}):null,s("div",{class:"vp-feature",style:o?{color:o}:{}},[((l=t.image)==null?void 0:l.call(t,e))||[u?s("img",{class:["vp-feature-image",{light:d}],src:Le(u),alt:p}):null,d?s("img",{class:"vp-feature-image dark",src:Le(d),alt:p}):null],((n=t.info)==null?void 0:n.call(t,e))||[p?s("h2",{class:"vp-feature-header"},p):null,c?s("p",{class:"vp-feature-description",innerHTML:c}):null],h.length?s("div",{class:"vp-features"},h.map(({icon:f,title:b,details:w,link:x})=>{const g=[s("h3",{class:"vp-feature-title"},[s(Ne,{icon:f}),s("span",{innerHTML:b})]),s("p",{class:"vp-feature-details",innerHTML:w})];return x?Bn(x)?s("a",{class:"vp-feature-item link",href:x,role:"navigation","aria-label":b,target:"_blank"},g):s(Re,{class:"vp-feature-item link",to:x,role:"navigation","aria-label":b},()=>g):s("div",{class:"vp-feature-item"},g)})):null])])};pu.displayName="FeaturePanel";var _s=pu,jv=M({name:"HeroInfo",slots:Object,setup(e,{slots:t}){const l=Ee(),n=rn(),a=E(()=>l.value.heroFullScreen??!1),i=E(()=>{const{heroText:u,tagline:d}=l.value;return{text:u??n.value.title??"Hello",tagline:d??n.value.description??"",isFullScreen:a.value}}),r=E(()=>{const{heroText:u,heroImage:d,heroImageDark:p,heroAlt:h,heroImageStyle:f}=l.value;return{image:d?Le(d):null,imageDark:p?Le(p):null,heroStyle:f,alt:h||u||"hero image",isFullScreen:a.value}}),o=E(()=>{const{bgImage:u,bgImageDark:d,bgImageStyle:p}=l.value;return{image:Tt(u)?Le(u):null,imageDark:Tt(d)?Le(d):null,bgStyle:p,isFullScreen:a.value}}),c=E(()=>l.value.actions??[]);return()=>{var u,d,p;return s("header",{class:["vp-hero-info-wrapper",{fullscreen:a.value}]},[((u=t.heroBg)==null?void 0:u.call(t,o.value))||[o.value.image?s("div",{class:["vp-hero-mask",{light:o.value.imageDark}],style:[{"background-image":`url(${o.value.image})`},o.value.bgStyle]}):null,o.value.imageDark?s("div",{class:"vp-hero-mask dark",style:[{"background-image":`url(${o.value.imageDark})`},o.value.bgStyle]}):null],s("div",{class:"vp-hero-info"},[((d=t.heroImage)==null?void 0:d.call(t,r.value))||s(pe,{appear:!0,type:"group"},()=>[r.value.image?s("img",{key:"light",class:["vp-hero-image",{light:r.value.imageDark}],style:r.value.heroStyle,src:r.value.image,alt:r.value.alt}):null,r.value.imageDark?s("img",{key:"dark",class:"vp-hero-image dark",style:r.value.heroStyle,src:r.value.imageDark,alt:r.value.alt}):null]),((p=t.heroInfo)==null?void 0:p.call(t,i.value))??s("div",{class:"vp-hero-infos"},[i.value.text?s(pe,{appear:!0,delay:.04},()=>s("h1",{id:"main-title"},i.value.text)):null,i.value.tagline?s(pe,{appear:!0,delay:.08},()=>s("p",{class:"vp-description",innerHTML:i.value.tagline})):null,c.value.length?s(pe,{appear:!0,delay:.12},()=>s("p",{class:"vp-actions"},c.value.map(h=>s(He,{class:["vp-action",h.type||"default"],config:h,noExternalLinkIcon:!0})))):null])])])}}});const hu=(e,{slots:t})=>{var l,n,a;const{bgImage:i,bgImageDark:r,bgImageStyle:o,color:c,description:u,image:d,imageDark:p,header:h,highlights:f=[],type:b="un-order"}=e;return s("div",{class:"vp-highlight-wrapper",style:c?{color:c}:{}},[i?s("div",{class:["vp-highlight-bg",{light:r}],style:[{"background-image":`url(${i})`},o]}):null,r?s("div",{class:"vp-highlight-bg dark",style:[{"background-image":`url(${r})`},o]}):null,s("div",{class:"vp-highlight"},[((l=t.image)==null?void 0:l.call(t,e))||[d?s("img",{class:["vp-highlight-image",{light:p}],src:Le(d),alt:h}):null,p?s("img",{class:"vp-highlight-image dark",src:Le(p),alt:h}):null],((n=t.info)==null?void 0:n.call(t,e))||[s("div",{class:"vp-highlight-info-wrapper"},s("div",{class:"vp-highlight-info"},[h?s("h2",{class:"vp-highlight-header",innerHTML:h}):null,u?s("p",{class:"vp-highlight-description",innerHTML:u}):null,((a=t.highlights)==null?void 0:a.call(t,f))||s(b==="order"?"ol":b==="no-order"?"dl":"ul",{class:"vp-highlights"},f.map(({icon:w,title:x,details:g,link:_})=>{const P=[s(b==="no-order"?"dt":"h3",{class:"vp-highlight-title"},[w?s(Ne,{class:"vp-highlight-icon",icon:w}):null,s("span",{innerHTML:x})]),g?s(b==="no-order"?"dd":"p",{class:"vp-highlight-details",innerHTML:g}):null];return s(b==="no-order"?"div":"li",{class:["vp-highlight-item-wrapper",{link:_}]},_?G1(_)?s("a",{class:"vp-highlight-item link",href:_,role:"navigation","aria-label":x,target:"_blank"},P):s(Re,{class:"vp-highlight-item link",to:_,role:"navigation","aria-label":x},()=>P):s("div",{class:"vp-highlight-item"},P))}))]))]])])};hu.displayName="HighlightPanel";var Bv=hu,zv=M({name:"HomePage",slots:Object,setup(e,{slots:t}){const l=Il(),n=Ee(),a=E(()=>{const{features:r}=n.value;return X(r)?r:null}),i=E(()=>{const{highlights:r}=n.value;return X(r)?r:null});return()=>{var r,o,c,u;return s("main",{id:"main-content",class:["vp-project-home ",{pure:l.value}],"aria-labelledby":n.value.heroText===null?"":"main-title"},[(r=t.top)==null?void 0:r.call(t),s(jv),((o=i.value)==null?void 0:o.map(d=>"features"in d?s(_s,d):s(Bv,d)))||(a.value?s(pe,{appear:!0,delay:.24},()=>s(_s,{features:a.value})):null),(c=t.center)==null?void 0:c.call(t),s(pe,{appear:!0,delay:.32},()=>s(ji)),(u=t.bottom)==null?void 0:u.call(t)])}}}),Hv=M({name:"BreadCrumb",setup(){const e=Ve(),t=ue(),l=mt(),n=Ee(),a=ie(),i=Ue([]),r=E(()=>(n.value.breadcrumb||n.value.breadcrumb!==!1&&a.value.breadcrumb!==!1)&&i.value.length>1),o=E(()=>n.value.breadcrumbIcon||n.value.breadcrumbIcon!==!1&&a.value.breadcrumbIcon!==!1),c=()=>{const u=e.getRoutes(),d=Nv(t.value.path,l.value).map(({link:p,name:h})=>{const f=u.find(b=>b.path===p);if(f){const{meta:b,path:w}=_l(e,f.path);return{title:b[be.shortTitle]||b[be.title]||h,icon:b[be.icon],path:w}}return null}).filter(p=>p!==null);d.length>1&&(i.value=d)};return ke(()=>{c(),ce(()=>t.value.path,c)}),()=>s("nav",{class:["vp-breadcrumb",{disable:!r.value}]},r.value?s("ol",{vocab:"https://schema.org/",typeof:"BreadcrumbList"},i.value.map((u,d)=>s("li",{class:{"is-active":i.value.length-1===d},property:"itemListElement",typeof:"ListItem"},[s(Re,{to:u.path,property:"item",typeof:"WebPage"},()=>[o.value?s(Ne,{icon:u.icon}):null,s("span",{property:"name"},u.title||"Unknown")]),s("meta",{property:"position",content:d+1})]))):[])}});const ks=e=>{const t=Ve();return e===!1?!1:ae(e)?pl(t,e,!0):xi(e)?e:null},Qa=(e,t,l)=>{const n=e.findIndex(a=>a.link===t);if(n!==-1){const a=e[n+l];return a!=null&&a.link?a:null}for(const a of e)if(a.children){const i=Qa(a.children,t,l);if(i)return i}return null};var qv=M({name:"PageNav",setup(){const e=ie(),t=Ee(),l=Fi(),n=ue(),a=un(),i=E(()=>{const o=ks(t.value.prev);return o===!1?null:o||(e.value.prevLink===!1?null:Qa(l.value,n.value.path,-1))}),r=E(()=>{const o=ks(t.value.next);return o===!1?null:o||(e.value.nextLink===!1?null:Qa(l.value,n.value.path,1))});return Me("keydown",o=>{o.altKey&&(o.key==="ArrowRight"?r.value&&(a(r.value.link),o.preventDefault()):o.key==="ArrowLeft"&&i.value&&(a(i.value.link),o.preventDefault()))}),()=>i.value||r.value?s("nav",{class:"vp-page-nav"},[i.value?s(He,{class:"prev",config:i.value},()=>{var o,c;return[s("div",{class:"hint"},[s("span",{class:"arrow start"}),e.value.metaLocales.prev]),s("div",{class:"link"},[s(Ne,{icon:(o=i.value)==null?void 0:o.icon}),(c=i.value)==null?void 0:c.text])]}):null,r.value?s(He,{class:"next",config:r.value},()=>{var o,c;return[s("div",{class:"hint"},[e.value.metaLocales.next,s("span",{class:"arrow end"})]),s("div",{class:"link"},[(o=r.value)==null?void 0:o.text,s(Ne,{icon:(c=r.value)==null?void 0:c.icon})])]}):null]):null}});const Uv={GitHub:":repo/edit/:branch/:path",GitLab:":repo/-/edit/:branch/:path",Gitee:":repo/edit/:branch/:path",Bitbucket:":repo/src/:branch/:path?mode=edit&spa=0&at=:branch&fileviewer=file-view-default"},Gv=({docsRepo:e,docsBranch:t,docsDir:l,filePathRelative:n,editLinkPattern:a})=>{if(!n)return null;const i=rc(e);let r;return a?r=a:i!==null&&(r=Uv[i]),r?r.replace(/:repo/,Zt(e)?e:`https://github.com/${e}`).replace(/:branch/,t).replace(/:path/,Po(`${Li(l)}/${n}`)):null},Wv=()=>{const e=ie(),t=ue(),l=Ee();return E(()=>{const{repo:n,docsRepo:a=n,docsBranch:i="main",docsDir:r="",editLink:o,editLinkPattern:c=""}=e.value;if(!(l.value.editLink??o??!0)||!a)return null;const u=Gv({docsRepo:a,docsBranch:i,docsDir:r,editLinkPattern:c,filePathRelative:t.value.filePathRelative});return u?{text:e.value.metaLocales.editLink,link:u}:null})},Kv=()=>{const e=rn(),t=ie(),l=ue(),n=Ee();return E(()=>{var a,i;return!(n.value.lastUpdated??t.value.lastUpdated??!0)||!((a=l.value.git)!=null&&a.updatedTime)?null:new Date((i=l.value.git)==null?void 0:i.updatedTime).toLocaleString(e.value.lang)})},Jv=()=>{const e=ie(),t=ue(),l=Ee();return E(()=>{var n;return l.value.contributors??e.value.contributors??!0?((n=t.value.git)==null?void 0:n.contributors)??null:null})};var Qv=M({name:"PageTitle",setup(){const e=ue(),t=Ee(),l=ie(),{info:n,items:a}=Zf();return()=>s("div",{class:"vp-page-title"},[s("h1",[l.value.titleIcon===!1?null:s(Ne,{icon:t.value.icon}),e.value.title]),s(cu,{info:n.value,...a.value===null?{}:{items:a.value}}),s("hr")])}});const fu=()=>s(de,{name:"edit"},()=>[s("path",{d:"M430.818 653.65a60.46 60.46 0 0 1-50.96-93.281l71.69-114.012 7.773-10.365L816.038 80.138A60.46 60.46 0 0 1 859.225 62a60.46 60.46 0 0 1 43.186 18.138l43.186 43.186a60.46 60.46 0 0 1 0 86.373L588.879 565.55l-8.637 8.637-117.466 68.234a60.46 60.46 0 0 1-31.958 11.229z"}),s("path",{d:"M728.802 962H252.891A190.883 190.883 0 0 1 62.008 771.98V296.934a190.883 190.883 0 0 1 190.883-192.61h267.754a60.46 60.46 0 0 1 0 120.92H252.891a69.962 69.962 0 0 0-69.098 69.099V771.98a69.962 69.962 0 0 0 69.098 69.098h475.911A69.962 69.962 0 0 0 797.9 771.98V503.363a60.46 60.46 0 1 1 120.922 0V771.98A190.883 190.883 0 0 1 728.802 962z"})]);fu.displayName="EditIcon";var Yv=M({name:"PageMeta",setup(){const e=ie(),t=Wv(),l=Kv(),n=Jv();return()=>{const{metaLocales:a}=e.value;return s("footer",{class:"page-meta"},[t.value?s("div",{class:"meta-item edit-link"},s(He,{class:"label",config:t.value},{before:()=>s(fu)})):null,s("div",{class:"meta-item git-info"},[l.value?s("div",{class:"update-time"},[s("span",{class:"label"},`${a.lastUpdated}: `),s(Zn,()=>s("span",{class:"info"},l.value))]):null,n.value&&n.value.length?s("div",{class:"contributors"},[s("span",{class:"label"},`${a.contributors}: `),n.value.map(({email:i,name:r},o)=>[s("span",{class:"contributor",title:`email: ${i}`},r),o!==n.value.length-1?",":""])]):null])])}}}),Xv=M({name:"NormalPage",slots:Object,setup(e,{slots:t}){const l=Ee(),n=ue(),{isDarkmode:a}=pn(),i=ie(),r=E(()=>l.value.toc||l.value.toc!==!1&&i.value.toc!==!1);return()=>s("main",{id:"main-content",class:"vp-page"},s(ct("LocalEncrypt")?Je("LocalEncrypt"):Xo,()=>{var o,c,u,d;return[(o=t.top)==null?void 0:o.call(t),l.value.cover?s("img",{class:"page-cover",src:Le(l.value.cover),alt:n.value.title,"no-view":""}):null,s(Hv),s(Qv),r.value?s(uu,{headerDepth:l.value.headerDepth??i.value.headerDepth??2},{before:()=>{var p;return(p=t.tocBefore)==null?void 0:p.call(t)},after:()=>{var p;return(p=t.tocAfter)==null?void 0:p.call(t)}}):null,(c=t.contentBefore)==null?void 0:c.call(t),s(ji),(u=t.contentAfter)==null?void 0:u.call(t),s(Yv),s(qv),ct("CommentService")?s(Je("CommentService"),{darkmode:a.value}):null,(d=t.bottom)==null?void 0:d.call(t)]}))}}),Zv=M({name:"Layout",setup(){const e=el(),t=ie(),l=ue(),n=Ee(),{isMobile:a}=dn(),i=E(()=>{var r,o;return((r=t.value.blog)==null?void 0:r.sidebarDisplay)||((o=e.value.blog)==null?void 0:o.sidebarDisplay)||"mobile"});return()=>[s(Bi),s(Ni,{},{default:()=>n.value.home?s(zv):s(Fv,()=>s(Xv,{key:l.value.path})),...i.value!=="none"?{navScreenBottom:()=>s(Je("BloggerInfo"))}:{},...!a.value&&i.value==="always"?{sidebar:()=>s(Je("BloggerInfo"))}:{}})]}}),e2=M({name:"NotFoundHint",setup(){const e=ie(),t=()=>{const l=e.value.routeLocales.notFoundMsg;return l[Math.floor(Math.random()*l.length)]};return()=>s("div",{class:"not-found-hint"},[s("p",{class:"error-code"},"404"),s("h1",{class:"error-title"},e.value.routeLocales.notFoundTitle),s("p",{class:"error-hint"},t())])}}),t2=M({name:"NotFound",slots:Object,setup(e,{slots:t}){const l=mt(),n=ie(),{navigate:a}=qa({to:n.value.home??l.value});return()=>[s(Bi),s(Ni,{noSidebar:!0},()=>{var i;return s("main",{id:"main-content",class:"vp-page not-found"},((i=t.default)==null?void 0:i.call(t))||[s(e2),s("div",{class:"actions"},[s("button",{type:"button",class:"action-button",onClick:()=>{window.history.go(-1)}},n.value.routeLocales.back),s("button",{type:"button",class:"action-button",onClick:()=>a()},n.value.routeLocales.home)])])})]}});const l2={GitHub:'',BiliBili:'',Email:'',Rss:''},n2={category:{"/":{path:"/category/",map:{}}},tag:{"/":{path:"/tag/",map:{Daily:{path:"/tag/daily/",keys:["v-538a98d7","v-2f6ae09c","v-76f251d2","v-bd2b5fe8","v-75616b85","v-f5471cb0","v-5c48d497","v-fd7b7f92","v-b5a78a7a","v-81a71778","v-ca672354","v-7a74360a","v-1aafac08","v-f297935a","v-143a8bce","v-7e2c7a0c","v-79e139f8","v-f1efc11c","v-f47f129e","v-31eac486","v-30a50b00","v-0481cc80","v-1e305501","v-6dc18ec6","v-86a15cb6"]},Frontend:{path:"/tag/frontend/",keys:["v-30a50b00","v-1e305501","v-7320140c","v-72e84a92"]},Video:{path:"/tag/video/",keys:["v-5c48d497","v-ca672354","v-7a74360a","v-1aafac08","v-79e139f8","v-404740fa","v-f1efc11c","v-f47f129e","v-414fd2b2","v-30a50b00","v-0481cc80","v-1e305501"]},Linux:{path:"/tag/linux/",keys:["v-414fd2b2","v-31eac486"]},Docker:{path:"/tag/docker/",keys:["v-414fd2b2","v-31eac486"]},MySQL:{path:"/tag/mysql/",keys:["v-2f6ae09c","v-f297935a","v-143a8bce","v-7e2c7a0c","v-79e139f8"]},AI:{path:"/tag/ai/",keys:["v-81a71778","v-7a74360a","v-404740fa","v-221efd1f"]},Emotion:{path:"/tag/emotion/",keys:["v-f47f129e"]},"Working Experience":{path:"/tag/working-experience/",keys:["v-fd7b7f92","v-f47f129e"]},Tool:{path:"/tag/tool/",keys:["v-404740fa","v-17809471"]},Design:{path:"/tag/design/",keys:["v-bd2b5fe8"]},English:{path:"/tag/english/",keys:["v-221efd1f","v-6ed7d996","v-860c51c8","v-89760306","v-8cdfb444","v-d42db13c","v-ae153a4e","v-788d194a","v-58a409d7","v-3ac0474c","v-90496582","v-246f4f17","v-93b316c0","v-971cc7fe"]},Git:{path:"/tag/git/",keys:["v-60021cbc","v-071be141","v-4008ff77","v-48e494ef","v-0fbf5fdc","v-1e5872c4","v-642eaaea"]},GitLab:{path:"/tag/gitlab/",keys:["v-071be141","v-4008ff77"]},Java:{path:"/tag/java/",keys:["v-538a98d7","v-2f6ae09c","v-f5471cb0","v-b5a78a7a","v-ca672354","v-1aafac08","v-100d1814","v-65b23736","v-4008ff77","v-6dc18ec6","v-86a15cb6"]},"Node.js":{path:"/tag/node.js/",keys:["v-75616b85","v-0be1af08","v-4008ff77","v-c488ac58","v-efcacba2"]},Python:{path:"/tag/python/",keys:["v-0be1af08","v-071be141","v-5b37b3c6"]},JavaScript:{path:"/tag/javascript/",keys:["v-538a98d7"]},Testing:{path:"/tag/testing/",keys:["v-113531b4","v-65b23736","v-0be1af08","v-c488ac58","v-efcacba2"]}}}}},a2={article:{"/":{path:"/article/",keys:["v-538a98d7","v-2f6ae09c","v-76f251d2","v-bd2b5fe8","v-75616b85","v-f5471cb0","v-5c48d497","v-fd7b7f92","v-b5a78a7a","v-81a71778","v-ca672354","v-7a74360a","v-1aafac08","v-f297935a","v-143a8bce","v-7e2c7a0c","v-79e139f8","v-100d1814","v-60021cbc","v-404740fa","v-f1efc11c","v-f47f129e","v-414fd2b2","v-31eac486","v-30a50b00","v-0481cc80","v-1e305501","v-113531b4","v-65b23736","v-0be1af08","v-221efd1f","v-071be141","v-5b37b3c6","v-4008ff77","v-6dc18ec6","v-86a15cb6","v-6ed7d996","v-860c51c8","v-89760306","v-8cdfb444","v-d42db13c","v-ae153a4e","v-788d194a","v-58a409d7","v-3ac0474c","v-90496582","v-246f4f17","v-17809471","v-93b316c0","v-971cc7fe","v-48e494ef","v-0fbf5fdc","v-7320140c","v-c488ac58","v-1e5872c4","v-642eaaea","v-72e84a92","v-efcacba2"]}},star:{"/":{path:"/star/",keys:[]}},timeline:{"/":{path:"/timeline/",keys:["v-538a98d7","v-2f6ae09c","v-76f251d2","v-bd2b5fe8","v-75616b85","v-f5471cb0","v-5c48d497","v-fd7b7f92","v-b5a78a7a","v-81a71778","v-ca672354","v-7a74360a","v-1aafac08","v-f297935a","v-143a8bce","v-7e2c7a0c","v-79e139f8","v-100d1814","v-60021cbc","v-404740fa","v-f1efc11c","v-f47f129e","v-414fd2b2","v-31eac486","v-30a50b00","v-0481cc80","v-1e305501","v-113531b4","v-65b23736","v-0be1af08","v-221efd1f","v-071be141","v-5b37b3c6","v-4008ff77","v-6dc18ec6","v-86a15cb6","v-6ed7d996","v-860c51c8","v-89760306","v-8cdfb444","v-d42db13c","v-ae153a4e","v-788d194a","v-58a409d7","v-3ac0474c","v-90496582","v-246f4f17","v-17809471","v-93b316c0","v-971cc7fe","v-48e494ef","v-0fbf5fdc","v-7320140c","v-c488ac58","v-1e5872c4","v-642eaaea","v-72e84a92","v-efcacba2"]}}},ws=W(n2),vu=(e="")=>{const t=ue(),l=Ve(),n=mt();return E(()=>{var a;const i=e||((a=Ee().value.blog)==null?void 0:a.key)||"";if(!i)return console.warn("useBlogCategory: key not found"),{path:"/",map:{}};const r=l.getRoutes();if(!ws.value[i])throw new Error(`useBlogCategory: key ${i} is invalid`);const o=ws.value[i][n.value],c={path:o.path,map:{}};for(const u in o.map){const d=o.map[u];c.map[u]={path:d.path,items:[]};for(const p of d.keys){const h=r.find(({name:f})=>f===p);if(h){const f=_l(l,h.path);c.map[u].items.push({path:f.path,info:f.meta})}}t.value.path===d.path&&(c.currentItems=c.map[u].items)}return c})},Es=W(a2),ia=(e="")=>{const t=Ve(),l=mt();return E(()=>{var n;const a=e||((n=Ee().value.blog)==null?void 0:n.key)||"";if(!a)return console.warn("useBlogType: key not found"),{path:"/",items:[]};if(!Es.value[a])throw new Error(`useBlogType: key ${e} is invalid`);const i=t.getRoutes(),r=Es.value[a][l.value],o={path:r.path,items:[]};for(const c of r.keys){const u=i.find(({name:d})=>d===c);if(u){const d=_l(t,u.path);o.items.push({path:d.path,info:d.meta})}}return o})};const i2="/assets/hero-197a9d2d.jpg",gu=Symbol.for("categoryMap"),hn=()=>{const e=me(gu);if(!e)throw new Error("useCategoryMap() is called without provider.");return e},r2=()=>{const e=vu("category");ot(gu,e)},fn=()=>{const e=el(),t=ie();return E(()=>({...e.value.blog,...t.value.blog}))},mu=Symbol.for("tagMap"),vn=()=>{const e=me(mu);if(!e)throw new Error("useTagMap() is called without provider.");return e},s2=()=>{const e=vu("tag");ot(mu,e)},o2=e=>{const t=ie();return E(()=>{const{[be.author]:l}=e.value;return l?Yl(l):l===!1?[]:Yl(t.value.author,!1)})},c2=e=>{const t=hn();return E(()=>nc(e.value[be.category]).map(l=>({name:l,path:t.value.map[l].path})))},u2=e=>{const t=vn();return E(()=>ac(e.value[be.tag]).map(l=>({name:l,path:t.value.map[l].path})))},d2=e=>E(()=>{const{[be.date]:t}=e.value;return Pi(t)}),p2=e=>{const t=Ll(e,"info"),l=fn(),n=o2(t),a=c2(t),i=u2(t),r=d2(t),o=Sc(),c=E(()=>({author:n.value,category:a.value,date:r.value,localizedDate:t.value[be.localizedDate]||"",tag:i.value,isOriginal:t.value[be.isOriginal]||!1,readingTime:t.value[be.readingTime]||null,readingTimeLocale:t.value[be.readingTime]&&o.value?Rc(t.value[be.readingTime],o.value):null,pageview:e.path})),u=E(()=>l.value.articleInfo);return{info:c,items:u}},yu=Symbol(""),gn=()=>{const e=me(yu);if(!e)throw new Error("useArticles() is called without provider.");return e},h2=()=>{const e=ia("article");ot(yu,e)},bu=Symbol(""),zi=()=>{const e=me(bu);if(!e)throw new Error("useStars() is called without provider.");return e},f2=()=>{const e=ia("star");ot(bu,e)},_u=Symbol(""),Hi=()=>{const e=me(_u);if(!e)throw new Error("useTimelines() is called without provider.");return e},v2=()=>{const e=ia("timeline"),t=E(()=>{const l=[];return e.value.items.forEach(({info:n,path:a})=>{const i=Pi(n[be.date]),r=i==null?void 0:i.getFullYear(),o=i?i.getMonth()+1:null,c=i==null?void 0:i.getDate();r&&o&&c&&((!l[0]||l[0].year!==r)&&l.unshift({year:r,items:[]}),l[0].items.push({date:`${o}/${c}`,info:n,path:a}))}),{...e.value,config:l.reverse()}});ot(_u,t)},g2=()=>{h2(),r2(),f2(),s2(),v2()};var m2=M({name:"SocialMedia",setup(){const e=fn(),t=Il(),l=E(()=>{const n=e.value.medias;return n?sn(n).map(([a,i])=>({name:a,icon:l2[a],url:i})):[]});return()=>l.value.length?s("div",{class:"vp-social-medias"},l.value.map(({name:n,icon:a,url:i})=>s("a",{class:"vp-social-media",href:i,rel:"noopener noreferrer",target:"_blank","aria-label":n,...t.value?{}:{"data-balloon-pos":"up"},innerHTML:a}))):null}}),qi=M({name:"BloggerInfo",setup(){const e=fn(),t=rn(),l=ie(),n=gn(),a=hn(),i=vn(),r=Hi(),o=un(),c=E(()=>{var h;return e.value.name||((h=Yl(l.value.author)[0])==null?void 0:h.name)||t.value.title}),u=E(()=>e.value.avatar||l.value.logo),d=E(()=>l.value.blogLocales),p=E(()=>e.value.intro);return()=>{const{article:h,category:f,tag:b,timeline:w}=d.value,x=[[n.value.path,n.value.items.length,h],[a.value.path,gt(a.value.map).length,f],[i.value.path,gt(i.value.map).length,b],[r.value.path,r.value.items.length,w]];return s("div",{class:"vp-blogger-info",vocab:"https://schema.org/",typeof:"Person"},[s("div",{class:"vp-blogger",...p.value?{style:{cursor:"pointer"},"aria-label":d.value.intro,"data-balloon-pos":"down",role:"navigation",onClick:()=>o(p.value)}:{}},[u.value?s("img",{class:["vp-blogger-avatar",{round:e.value.roundAvatar}],src:Le(u.value),property:"image",alt:"Blogger Avatar"}):null,c.value?s("div",{class:"vp-blogger-name",property:"name"},c.value):null,e.value.description?s("div",{class:"vp-blogger-description",innerHTML:e.value.description}):null,p.value?s("meta",{property:"url",content:Le(p.value)}):null]),s("div",{class:"vp-blog-counts"},x.map(([g,_,P])=>s(Re,{class:"vp-blog-count",to:g},()=>[s("div",{class:"count"},_),s("div",P)]))),s(m2)])}}});const Ya=()=>s(de,{name:"category"},()=>s("path",{d:"M148.41 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H148.41c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.311-40.31zM147.556 553.478H429.73c22.263 0 40.311 18.048 40.311 40.31v282.176c0 22.263-18.048 40.312-40.31 40.312H147.555c-22.263 0-40.311-18.049-40.311-40.312V593.79c0-22.263 18.048-40.311 40.31-40.311zM593.927 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H593.927c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.31-40.31zM730.22 920.502H623.926c-40.925 0-74.22-33.388-74.22-74.425V623.992c0-41.038 33.387-74.424 74.425-74.424h222.085c41.038 0 74.424 33.226 74.424 74.067v114.233c0 10.244-8.304 18.548-18.547 18.548s-18.548-8.304-18.548-18.548V623.635c0-20.388-16.746-36.974-37.33-36.974H624.13c-20.585 0-37.331 16.747-37.331 37.33v222.086c0 20.585 16.654 37.331 37.126 37.331H730.22c10.243 0 18.547 8.304 18.547 18.547 0 10.244-8.304 18.547-18.547 18.547z"}));Ya.displayName="CategoryIcon";const Xa=()=>s(de,{name:"tag"},()=>s("path",{d:"M939.902 458.563L910.17 144.567c-1.507-16.272-14.465-29.13-30.737-30.737L565.438 84.098h-.402c-3.215 0-5.726 1.005-7.634 2.913l-470.39 470.39a10.004 10.004 0 000 14.164l365.423 365.424c1.909 1.908 4.42 2.913 7.132 2.913s5.223-1.005 7.132-2.913l470.39-470.39c2.01-2.11 3.014-5.023 2.813-8.036zm-240.067-72.121c-35.458 0-64.286-28.828-64.286-64.286s28.828-64.285 64.286-64.285 64.286 28.828 64.286 64.285-28.829 64.286-64.286 64.286z"}));Xa.displayName="TagIcon";const Ui=()=>s(de,{name:"timeline"},()=>s("path",{d:"M511.997 70.568c-243.797 0-441.429 197.633-441.429 441.435 0 243.797 197.632 441.429 441.43 441.429S953.431 755.8 953.431 512.002c0-243.796-197.637-441.434-441.435-441.434zm150.158 609.093-15.605 15.61c-8.621 8.615-22.596 8.615-31.215 0L472.197 552.126c-4.95-4.944-4.34-14.888-4.34-24.677V247.14c0-12.19 9.882-22.07 22.07-22.07h22.07c12.19 0 22.07 9.882 22.07 22.07v273.218l128.088 128.088c8.62 8.62 8.62 22.595 0 31.215zm0 0"}));Ui.displayName="TimelineIcon";const ku=()=>s(de,{name:"slides"},()=>s("path",{d:"M896 170.667v426.666a85.333 85.333 0 0 1-85.333 85.334h-256v61.184l192.597 115.584-43.861 73.13-148.736-89.173v95.275h-85.334v-95.318l-148.736 89.216-43.861-73.13 192.597-115.627v-61.141h-256A85.333 85.333 0 0 1 128 597.333V170.667H85.333V85.333h853.334v85.334H896zm-682.667 0v426.666h597.334V170.667H213.333zM426.667 512h-85.334V341.333h85.334V512zm128 0h-85.334V256h85.334v256zm128 0h-85.334V384h85.334v128z"}));ku.displayName="SlideIcon";const wu=()=>s(de,{name:"sticky"},()=>[s("path",{d:"m381.3 733.8l-161.9 118c-5.9 4.5-13.2 6.6-20.1 6.6-8.7 0-17.7-3.4-24.3-10-12.2-12.2-13.9-31.3-3.5-45.2l144.5-195.5-113.6-112.9c-11.1-11.1-13.2-28.4-5.5-42 5.5-8.7 52.1-76.4 155.5-51 1.8 0.3 3.5 0.3 5.6 0.7 4.2 0.3 9 0.7 14.2 1.7 21.9 3.5 60.8-13.9 94.5-42.7 32.3-27.5 53.1-59.4 53.1-81.6 0-5.2 0-10.8-0.3-16-0.7-20.8-2.1-52.8 21.5-76.4 28.1-28.1 72.9-30.6 103.9-5.2 0.6 0.3 1 1 1.7 1.7 16.7 16.3 187.5 187.2 189.3 188.9 14.5 14.6 22.9 34.4 22.9 55.3 0 20.8-8 40.2-22.9 54.8-23.7 23.6-56 22.6-77.1 21.6-4.9 0-10.5-0.4-15.7-0.4-20.8 0-45.8 14.6-70.5 41.3-34.3 37.5-55.5 85.8-53.8 107.7 0.7 6.9 2.1 19.1 2.4 20.8 25 101.4-42.7 147.6-50.7 152.8-13.9 8.4-31.6 6.3-42.7-4.8l-112.1-112.2z"})]);wu.displayName="StickyIcon";const qn=()=>s(de,{name:"article"},()=>s("path",{d:"M853.333 938.667H170.667A42.667 42.667 0 0 1 128 896V128a42.667 42.667 0 0 1 42.667-42.667h682.666A42.667 42.667 0 0 1 896 128v768a42.667 42.667 0 0 1-42.667 42.667zm-42.666-85.334V170.667H213.333v682.666h597.334zM298.667 256h170.666v170.667H298.667V256zm0 256h426.666v85.333H298.667V512zm0 170.667h426.666V768H298.667v-85.333zm256-384h170.666V384H554.667v-85.333z"}));qn.displayName="ArticleIcon";const Eu=()=>s(de,{name:"book"},()=>s("path",{d:"M256 853.333h426.667A85.333 85.333 0 0 0 768 768V256a85.333 85.333 0 0 0-85.333-85.333H469.333a42.667 42.667 0 0 1 0-85.334h213.334A170.667 170.667 0 0 1 853.333 256v512a170.667 170.667 0 0 1-170.666 170.667H213.333A42.667 42.667 0 0 1 170.667 896V128a42.667 42.667 0 0 1 42.666-42.667h128A42.667 42.667 0 0 1 384 128v304.256l61.653-41.088a42.667 42.667 0 0 1 47.36 0l61.654 41.045V256A42.667 42.667 0 0 1 640 256v256a42.667 42.667 0 0 1-66.347 35.499l-104.32-69.547-104.32 69.547A42.667 42.667 0 0 1 298.667 512V170.667H256v682.666z"}));Eu.displayName="BookIcon";const xu=()=>s(de,{name:"link"},()=>s("path",{d:"M460.8 584.533c17.067 17.067 17.067 42.667 0 59.734-17.067 17.066-42.667 17.066-59.733 0-85.334-85.334-85.334-217.6 0-302.934L554.667 192C640 110.933 776.533 110.933 857.6 196.267c81.067 81.066 81.067 213.333 0 294.4l-68.267 64c0-34.134-4.266-68.267-17.066-102.4l21.333-21.334c51.2-46.933 55.467-128 4.267-179.2s-128-55.466-179.2-4.266c-4.267 0-4.267 4.266-4.267 4.266L465.067 401.067c-51.2 51.2-51.2 132.266-4.267 183.466m123.733-183.466C601.6 384 627.2 384 644.267 401.067c85.333 85.333 85.333 217.6 0 302.933l-153.6 149.333C405.333 934.4 268.8 934.4 187.733 849.067c-81.066-81.067-81.066-213.334 0-294.4l68.267-64c0 34.133 4.267 72.533 17.067 102.4L251.733 614.4C204.8 665.6 204.8 746.667 256 793.6c51.2 46.933 123.733 46.933 174.933 0l149.334-149.333c51.2-51.2 51.2-128 0-179.2-12.8-17.067-17.067-46.934 4.266-64z"}));xu.displayName="LinkIcon";const Lu=()=>s(de,{name:"project"},()=>s("path",{d:"M987.456 425.152H864V295.296a36.48 36.48 0 0 0-36.544-36.544h-360l-134.08-128.256A9.344 9.344 0 0 0 327.04 128H36.48A36.48 36.48 0 0 0 0 164.544v676.608a36.48 36.48 0 0 0 36.544 36.544h797.76a36.672 36.672 0 0 0 33.92-22.848L1021.44 475.52a36.48 36.48 0 0 0-33.92-50.304zM82.304 210.304h215.424l136.64 130.752h347.328v84.096H198.848A36.672 36.672 0 0 0 164.928 448L82.304 652.8V210.304zM808.32 795.456H108.544l118.08-292.608h699.904L808.32 795.52z"}));Lu.displayName="ProjectIcon";const Tu=()=>s(de,{name:"friend"},()=>s("path",{d:"M860.16 213.333A268.373 268.373 0 0 0 512 186.027a267.52 267.52 0 0 0-348.16 404.48L428.8 855.893a118.613 118.613 0 0 0 166.4 0l264.96-265.386a267.52 267.52 0 0 0 0-377.174zM800 531.627l-264.96 264.96a32.427 32.427 0 0 1-46.08 0L224 530.347a183.04 183.04 0 0 1 0-256 182.187 182.187 0 0 1 256 0 42.667 42.667 0 0 0 60.587 0 182.187 182.187 0 0 1 256 0 183.04 183.04 0 0 1 3.413 256z"}));Tu.displayName="FriendIcon";const Za=()=>s(de,{name:"slide-down"},()=>s("path",{d:"M108.775 312.23c13.553 0 27.106 3.734 39.153 11.806l375.205 250.338 363.641-252.808c32.587-21.624 76.499-12.83 98.123 19.757 21.685 32.467 12.95 76.56-19.576 98.184l-402.854 278.89c-23.733 15.901-54.694 15.962-78.547.12L69.501 442.097c-32.647-21.685-41.441-65.777-19.817-98.304 13.734-20.54 36.201-31.563 59.09-31.563Z"}));Za.displayName="SlideDownIcon";const Au=()=>s("svg",{xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink",class:"empty-icon",viewBox:"0 0 1024 1024",innerHTML:''});Au.displayName="EmptyIcon";const Iu=()=>s(de,{name:"lock"},()=>s("path",{d:"M787.168 952.268H236.832c-30.395 0-55.033-24.638-55.033-55.033V429.45c0-30.395 24.638-55.034 55.033-55.034h82.55V264.35c0-106.38 86.238-192.618 192.618-192.618S704.618 157.97 704.618 264.35v110.066h82.55c30.395 0 55.033 24.639 55.033 55.034v467.785c0 30.395-24.639 55.033-55.033 55.033zM484.483 672.046v115.122h55.034V672.046c31.99-11.373 55.033-41.605 55.033-77.496 0-45.592-36.958-82.55-82.55-82.55s-82.55 36.958-82.55 82.55c0 35.89 23.042 66.123 55.033 77.496zM622.067 264.35c0-60.788-49.28-110.067-110.067-110.067s-110.067 49.28-110.067 110.067v110.066h220.135V264.35z"}));Iu.displayName="LockIcon";var y2=M({name:"ArticleItem",props:{info:{type:Object,required:!0},path:{type:String,required:!0}},slots:Object,setup(e,{slots:t}){const l=Ll(e,"info"),{info:n,items:a}=p2(e);return()=>{var i,r,o;const{[be.title]:c,[be.type]:u,[be.isEncrypted]:d=!1,[be.cover]:p,[be.excerpt]:h,[be.sticky]:f}=l.value,b=n.value;return s("div",{class:"vp-article-wrapper"},s("article",{class:"vp-article-item",vocab:"https://schema.org/",typeof:"Article"},[((i=t.cover)==null?void 0:i.call(t,{cover:p}))||(p?[s("img",{class:"vp-article-cover",src:Le(p)}),s("meta",{property:"image",content:Le(p)})]:[]),f?s(wu):null,s(Re,{to:e.path},()=>{var w;return((w=t.title)==null?void 0:w.call(t,{title:c,isEncrypted:d,type:u}))||s("header",{class:"vp-article-title"},[d?s(Iu):null,u===$c.slide?s(ku):null,s("span",{property:"headline"},c)])}),((r=t.excerpt)==null?void 0:r.call(t,{excerpt:h}))||(h?s("div",{class:"vp-article-excerpt",innerHTML:h}):null),s("hr",{class:"vp-article-hr"}),((o=t.info)==null?void 0:o.call(t,{info:b}))||s(cu,{info:b,...a.value?{items:a.value}:{}})]))}}}),b2=M({name:"Pagination",props:{total:{type:Number,default:10},perPage:{type:Number,default:10},current:{type:Number,default:1}},emits:["updateCurrentPage"],setup(e,{emit:t}){let l;const n=ie(),a=W(""),i=E(()=>n.value.paginationLocales),r=E(()=>Math.ceil(e.total/e.perPage)),o=E(()=>!!r.value&&r.value!==1),c=E(()=>r.value<7?!1:e.current>4),u=E(()=>r.value<7?!1:e.current{const{current:f}=e;let b=1,w=r.value;const x=[];r.value>=7&&(f<=4&&f4&&f>=r.value-3?(w=r.value,b=r.value-4):r.value>7&&(b=f-2,w=f+2));for(let g=b;g<=w;g++)x.push(g);return x}),p=f=>t("updateCurrentPage",f),h=f=>{const b=parseInt(f);b<=r.value&&b>0?p(b):l.pop(`${i.value.errorText.replace(/\$page/g,r.value.toString())}`)};return ke(()=>{l=new Y1}),()=>s("div",{class:"vp-pagination"},o.value?s("div",{class:"vp-pagination-list"},[s("div",{class:"vp-pagination-number "},[e.current>1?s("div",{class:"prev",role:"navigation",unselectable:"on",onClick:()=>p(e.current-1)},i.value.prev):null,c.value?[s("div",{role:"navigation",onClick:()=>p(1)},1),s("div",{class:"ellipsis"},"...")]:null,d.value.map(f=>s("div",{key:f,class:{active:e.current===f},role:"navigation",onClick:()=>p(f)},f)),u.value?[s("div",{class:"ellipsis"},"..."),s("div",{role:"navigation",onClick:()=>p(r.value)},r.value)]:null,e.currentp(e.current+1)},i.value.next):null]),s("div",{class:"vp-pagination-nav"},[s("label",{for:"navigation-text"},`${i.value.navigate}: `),s("input",{id:"navigation-text",value:a.value,onInput:({target:f})=>{a.value=f.value},onKeydown:f=>{f.key==="Enter"&&(f.preventDefault(),h(a.value))}}),s("button",{class:"vp-pagination-button",role:"navigation",title:i.value.action,onClick:()=>h(a.value)},i.value.action)])]):[])}}),Gi=M({name:"ArticleList",props:{items:{type:Array,default:()=>[]}},setup(e){const t=yt(),l=Ve(),n=fn(),a=W(1),i=E(()=>n.value.articlePerPage||10),r=E(()=>e.items.slice((a.value-1)*i.value,a.value*i.value)),o=c=>{a.value=c;const u={...t.query};u.page===c.toString()||c===1&&!u.page||(c===1?delete u.page:u.page=c.toString(),l.push({path:t.path,query:u}))};return ke(()=>{const{page:c}=t.query;o(c?Number(c):1),ce(a,()=>{const u=document.querySelector("#article-list").getBoundingClientRect().top+window.scrollY;setTimeout(()=>{window.scrollTo(0,u)},100)}),ce(()=>t.query,({page:u})=>{o(u?Number(u):1)})}),()=>s("div",{id:"article-list",class:"vp-article-list"},r.value.length?[...r.value.map(({info:c,path:u},d)=>s(pe,{appear:!0,delay:d*.04},()=>s(y2,{key:u,info:c,path:u}))),s(b2,{current:a.value,perPage:i.value,total:e.items.length,onUpdateCurrentPage:o})]:s(Au))}}),Wi=M({name:"CategoryList",setup(){const e=ue(),t=hn();return()=>s("ul",{class:"vp-category-list"},sn(t.value.map).map(([l,{path:n,items:a}])=>s("li",{class:["vp-category",`vp-category${la(l,9)}`,{active:n===e.value.path}]},s(Re,{to:n},()=>[l,s("span",{class:"count"},a.length)]))))}}),Ki=M({name:"TagList",setup(){const e=Ee(),t=vn(),l=n=>{var a;return n===((a=e.value.blog)==null?void 0:a.name)};return()=>s("ul",{class:"tag-list-wrapper"},sn(t.value.map).map(([n,{path:a,items:i}])=>s("li",{class:["tag",`tag${la(n,9)}`,{active:l(n)}]},s(Re,{to:a},()=>[n,s("span",{class:"tag-num"},i.length)]))))}}),_2=M({name:"TimelineList",setup(){const e=ie(),t=Hi(),l=un(),n=E(()=>e.value.blogLocales.timeline);return()=>s("div",{class:"timeline-list-wrapper"},[s("div",{class:"timeline-list-title",onClick:()=>l(t.value.path)},[s(Ui),s("span",{class:"num"},t.value.items.length),n.value]),s("hr"),s("div",{class:"timeline-content"},s("ul",{class:"timeline-list"},t.value.config.map(({year:a,items:i},r)=>s(pe,{appear:!0,delay:.08*(r+1)},()=>s("li",[s("h3",{class:"timeline-year"},a),s("ul",{class:"timeline-year-wrapper"},i.map(({date:o,info:c,path:u})=>s("li",{class:"timeline-item"},[s("span",{class:"timeline-date"},o),s(Re,{class:"timeline-title",to:u},()=>c[be.title])])))])))))])}}),Pu=M({name:"InfoList",setup(){const e=ie(),t=gn(),l=hn(),n=E(()=>gt(l.value.map).length),a=zi(),i=vn(),r=E(()=>gt(i.value.map).length),o=un(),c=W("article"),u=E(()=>e.value.blogLocales),d=[["article",qn],["category",Ya],["tag",Xa],["timeline",Ui]];return()=>s("div",{class:"vp-blog-infos"},[s("div",{class:"vp-blog-type-switcher"},d.map(([p,h])=>s("button",{type:"button",class:"vp-blog-type-button",onClick:()=>{c.value=p}},s("div",{class:["icon-wrapper",{active:c.value===p}],"aria-label":u.value[p],"data-balloon-pos":"up"},s(h))))),s(pe,()=>c.value==="article"?s("div",{class:"vp-sticky-article-wrapper"},[s("div",{class:"title",onClick:()=>o(t.value.path)},[s(qn),s("span",{class:"num"},t.value.items.length),u.value.article]),s("hr"),s("ul",{class:"vp-sticky-articles"},a.value.items.map(({info:p,path:h},f)=>s(pe,{appear:!0,delay:.08*(f+1)},()=>s("li",{class:"vp-sticky-article"},s(Re,{to:h},()=>p[be.title])))))]):c.value==="category"?s("div",{class:"vp-category-wrapper"},[n.value?s("div",{class:"title",onClick:()=>o(l.value.path)},[s(Ya),s("span",{class:"num"},n.value),u.value.category]):null,s("hr"),s(pe,{delay:.04},()=>s(Wi))]):c.value==="tag"?s("div",{class:"vp-tag-wrapper"},[r.value?s("div",{class:"title",onClick:()=>o(i.value.path)},[s(Xa),s("span",{class:"num"},r.value),u.value.tag]):null,s("hr"),s(pe,{delay:.04},()=>s(Ki))]):s(pe,()=>s(_2)))])}}),ra=M({name:"BlogWrapper",slots:Object,setup(e,{slots:t}){const{isMobile:l}=dn();return()=>[s(Bi),s(Ni,{noSidebar:!0,noToc:!0},{default:()=>t.default(),navScreenBottom:()=>s(qi),...l.value?{sidebar:()=>s(Pu)}:{}})]}});const Ou=()=>s("aside",{class:"vp-blog-info-wrapper"},[s(pe,()=>s(qi)),s(pe,{delay:.04},()=>s(Pu))]);Ou.displayName="InfoPanel";var sa=Ou,k2=M({name:"BlogPage",components:{CategoryList:Wi,TagList:Ki},setup(){const e=ue(),t=Ee(),l=hn(),n=vn(),a=E(()=>t.value.blog||{}),i=E(()=>{const{key:o=""}=a.value;return o==="category"?"CategoryList":o==="tag"?"TagList":null}),r=E(()=>{const{name:o="",key:c=""}=a.value;return c==="category"?o?l.value.map[o].items:[]:c==="tag"?o?n.value.map[o].items:[]:[]});return()=>s(ra,()=>s("div",{class:"vp-page vp-blog"},s("div",{class:"blog-page-wrapper"},[s("main",{id:"main-content",class:"vp-blog-main"},[s(pe,()=>i.value?s(Je(i.value)):null),a.value.name?s(pe,{appear:!0,delay:.24},()=>s(Gi,{key:e.value.path,items:r.value})):null]),s(pe,{delay:.16},()=>s(sa,{key:"blog"}))])))}}),w2=M({name:"BlogHero",slots:Object,setup(e,{slots:t}){const l=Ee(),n=rn(),a=Ue(),i=E(()=>l.value.heroFullScreen??!1),r=E(()=>{const{heroText:c,heroImage:u,heroImageDark:d,heroAlt:p,heroImageStyle:h,tagline:f}=l.value;return{text:c??n.value.title??"Hello",image:u?Le(u):null,imageDark:d?Le(d):null,heroStyle:h,alt:p||c||"hero image",tagline:f??"",isFullScreen:i.value}}),o=E(()=>{const{bgImage:c,bgImageDark:u,bgImageStyle:d}=l.value;return{image:ae(c)?Le(c):c===!1?null:i2,imageDark:ae(u)?Le(u):null,bgStyle:d,isFullScreen:i.value}});return()=>{var c,u;return l.value.hero===!1?null:s("div",{ref:a,class:["vp-blog-hero",{fullscreen:i.value,"no-bg":!o.value.image}]},[((c=t.heroBg)==null?void 0:c.call(t,o.value))||[o.value.image?s("div",{class:["vp-blog-mask",{light:o.value.imageDark}],style:[{background:`url(${o.value.image}) center/cover no-repeat`},o.value.bgStyle]}):null,o.value.imageDark?s("div",{class:"vp-blog-mask dark",style:[{background:`url(${o.value.imageDark}) center/cover no-repeat`},o.value.bgStyle]}):null],((u=t.heroInfo)==null?void 0:u.call(t,r.value))||[s(pe,{appear:!0,type:"group",delay:.04},()=>[r.value.image?s("img",{key:"light",class:["vp-blog-hero-image",{light:r.value.imageDark}],style:r.value.heroStyle,src:r.value.image,alt:r.value.alt}):null,r.value.imageDark?s("img",{key:"dark",class:"vp-blog-hero-image dark",style:r.value.heroStyle,src:r.value.imageDark,alt:r.value.alt}):null]),s(pe,{appear:!0,delay:.08},()=>r.value.text?s("h1",{class:"vp-blog-hero-title"},r.value.text):null),s(pe,{appear:!0,delay:.12},()=>r.value.tagline?s("p",{class:"vp-blog-hero-description",innerHTML:r.value.tagline}):null)],r.value.isFullScreen?s("button",{type:"button",class:"slide-down-button",onClick:()=>{window.scrollTo({top:a.value.clientHeight,behavior:"smooth"})}},[s(Za),s(Za)]):null])}}});const E2=["link","article","book","project","friend"];var x2=M({name:"ProjectPanel",components:{ArticleIcon:qn,BookIcon:Eu,FriendIcon:Tu,LinkIcon:xu,ProjectIcon:Lu},setup(){const e=Ee(),t=Il(),l=un(),n=(a="",i="icon")=>E2.includes(a)?s(Je(`${a}-icon`)):Zt(a)?s("img",{class:"vp-project-image",src:a,alt:i}):na(a)?s("img",{class:"vp-project-image",src:Le(a),alt:i}):s(Ne,{icon:a});return()=>{var a;return(a=e.value.projects)!=null&&a.length?s("div",{class:"vp-project-panel"},e.value.projects.map(({icon:i,link:r,name:o,desc:c},u)=>s("div",{class:["vp-project-card",{[`project${u%9}`]:!t.value}],onClick:()=>l(r)},[n(i,o),s("div",{class:"vp-project-name"},o),s("div",{class:"vp-project-desc"},c)]))):null}}}),L2=M({name:"BlogHome",setup(){const e=gn();return()=>s("div",{class:"vp-page vp-blog"},[s(w2),s("div",{class:"blog-page-wrapper"},[s("main",{id:"main-content",class:"vp-blog-main"},[s(pe,{appear:!0,delay:.16},()=>s(x2)),s(pe,{appear:!0,delay:.24},()=>s(Gi,{items:e.value.items}))]),s(pe,{appear:!0,delay:.16},()=>s(sa,{key:"blog"}))]),s(pe,{appear:!0,delay:.28},()=>s(ji))])}}),T2=M({name:"BlogHome",setup(){return()=>s(ra,()=>s(L2))}}),Cu=M({name:"ArticleType",setup(){const e=ue(),t=mt(),l=ie(),n=gn(),a=zi(),i=E(()=>{const r=l.value.blogLocales;return[{text:r.all,path:n.value.path},{text:r.star,path:a.value.path},...[].map(({key:o,path:c})=>({text:r[o],path:c.replace(/^\//,t.value)}))]});return()=>s("ul",{class:"vp-article-type-wrapper"},i.value.map(r=>s("li",{class:["vp-article-type",{active:r.path===e.value.path}]},s(Re,{to:r.path},()=>r.text))))}}),A2=M({name:"BlogPage",setup(){const e=ia(),t=Ee(),l=ue(),n=gn(),a=zi(),i=E(()=>{const{key:r="",type:o}=t.value.blog||{};return r==="star"?a.value.items:o==="type"&&r?e.value.items:n.value.items});return()=>s(ra,()=>s("div",{class:"vp-page vp-blog"},s("div",{class:"blog-page-wrapper"},[s("main",{id:"main-content",class:"vp-blog-main"},[s(pe,()=>s(Cu)),s(pe,{appear:!0,delay:.24},()=>s(Gi,{key:l.value.path,items:i.value}))]),s(pe,{delay:.16},()=>s(sa,{key:"blog"}))])))}}),I2=M({name:"TimelineItems",setup(){const e=fn(),t=ie(),l=Hi(),n=E(()=>e.value.timeline||t.value.blogLocales.timelineTitle),a=E(()=>l.value.config.map(({year:i})=>({title:i.toString(),level:2,slug:i.toString(),children:[]})));return()=>s("div",{class:"timeline-wrapper"},s("ul",{class:"timeline-content"},[s(pe,()=>s("li",{class:"motto"},n.value)),s(uu,{items:a.value}),l.value.config.map(({year:i,items:r},o)=>s(pe,{appear:!0,delay:.08*(o+1),type:"group"},()=>[s("h3",{key:"title",id:i,class:"timeline-year-title"},s("span",i)),s("li",{key:"content",class:"timeline-year-list"},[s("ul",{class:"timeline-year-wrapper"},r.map(({date:c,info:u,path:d})=>s("li",{class:"timeline-item"},[s("span",{class:"timeline-date"},c),s(Re,{class:"timeline-title",to:d},()=>u[be.title])])))])]))]))}}),P2=M({name:"Timeline",components:{ArticleType:Cu,CategoryList:Wi,TagList:Ki},setup(){return()=>s(ra,()=>s("div",{class:"vp-page vp-blog"},s("div",{class:"blog-page-wrapper"},[s("main",{id:"main-content",class:"vp-blog-main"},[s(pe,{appear:!0,delay:.24},()=>s(I2))]),s(pe,{delay:.16},()=>s(sa,{key:"blog"}))])))}});of(Ne);const O2=dt({enhance:({app:e,router:t})=>{const{scrollBehavior:l}=t.options;t.options.scrollBehavior=async(...n)=>(await du().wait(),l(...n)),lv(e),e.component("HopeIcon",Ne),e.component("VPLink",Re),e.component("BloggerInfo",qi)},setup:()=>{nv(),sv(),g2()},layouts:{Layout:Zv,NotFound:t2,BlogCategory:k2,BlogHome:T2,BlogType:A2,Timeline:P2}}),C2=e=>e instanceof Element?document.activeElement===e&&(["TEXTAREA","SELECT","INPUT"].includes(e.tagName)||e.hasAttribute("contenteditable")):!1,R2=(e,t)=>t.some(l=>{if(ae(l))return l===e.key;const{key:n,ctrl:a=!1,shift:i=!1,alt:r=!1}=l;return n===e.key&&a===e.ctrlKey&&i===e.shiftKey&&r===e.altKey}),S2=/[^\x00-\x7F]/,D2=e=>e.split(/\s+/g).map(t=>t.trim()).filter(t=>!!t),xs=e=>e.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),Ls=(e,t)=>{const l=t.join(" "),n=D2(e);if(S2.test(e))return n.some(r=>l.toLowerCase().indexOf(r)>-1);const a=e.endsWith(" ");return new RegExp(n.map((r,o)=>n.length===o+1&&!a?`(?=.*\\b${xs(r)})`:`(?=.*\\b${xs(r)}\\b)`).join("")+".+","gi").test(l)},$2=({input:e,hotKeys:t})=>{if(t.value.length===0)return;const l=n=>{e.value&&R2(n,t.value)&&!C2(n.target)&&(n.preventDefault(),e.value.focus())};ke(()=>{document.addEventListener("keydown",l)}),Yn(()=>{document.removeEventListener("keydown",l)})},M2=[{title:"levy's blog",headers:[],path:"/",pathLocale:"/",extraFields:[]},{title:"关于",headers:[],path:"/about.html",pathLocale:"/",extraFields:[]},{title:"VuePress2 娱乐视频",headers:[],path:"/daily/a-vuepress2-entertaining-video.html",pathLocale:"/",extraFields:[]},{title:"来自Navicat的侵权警告",headers:[],path:"/daily/a-warning-from-navicat.html",pathLocale:"/",extraFields:[]},{title:"关于 Arm 你需要了解的三件事",headers:[],path:"/daily/about-arm-things-you-need-to-know.html",pathLocale:"/",extraFields:[]},{title:"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?",headers:[{level:2,title:"Background",slug:"background",link:"#background",children:[]},{level:2,title:"utf8mb4(UTF-8 MultiByte 4-Byte)",slug:"utf8mb4-utf-8-multibyte-4-byte",link:"#utf8mb4-utf-8-multibyte-4-byte",children:[]},{level:2,title:"utf8mb4_unicode_ci",slug:"utf8mb4-unicode-ci",link:"#utf8mb4-unicode-ci",children:[]},{level:2,title:"Some tips",slug:"some-tips",link:"#some-tips",children:[]}],path:"/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html",pathLocale:"/",extraFields:[]},{title:"Claude AI应用案例,从HTML中抽取文本",headers:[],path:"/daily/claude-ai-in-action-extract-info-from-html.html",pathLocale:"/",extraFields:[]},{title:"复制代码也许不是罪",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"正文",slug:"正文",link:"#正文",children:[]}],path:"/daily/copy-code-may-not-be-guilty.html",pathLocale:"/",extraFields:[]},{title:"不要与傻逼进行争吵",headers:[],path:"/daily/dont-try-to-argue-with-a-sb.html",pathLocale:"/",extraFields:[]},{title:"迭代复盘之三员管理",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"动手前,至少梳理出接口清单",slug:"动手前-至少梳理出接口清单",link:"#动手前-至少梳理出接口清单",children:[]},{level:2,title:"迁移时,采取结队编程",slug:"迁移时-采取结队编程",link:"#迁移时-采取结队编程",children:[]},{level:2,title:"迁移后,需要对自己负责的功能设计测试用例",slug:"迁移后-需要对自己负责的功能设计测试用例",link:"#迁移后-需要对自己负责的功能设计测试用例",children:[]}],path:"/daily/iteration-retrospective-of-sanyuan.html",pathLocale:"/",extraFields:[]},{title:"都什么年代了,还在用传统方式写代码?",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"开发流程",slug:"开发流程",link:"#开发流程",children:[]},{level:2,title:"程序设计",slug:"程序设计",link:"#程序设计",children:[]},{level:2,title:"代码编写",slug:"代码编写",link:"#代码编写",children:[{level:3,title:"生成真实代码",slug:"生成真实代码",link:"#生成真实代码",children:[]},{level:3,title:"辅助编程工具",slug:"辅助编程工具",link:"#辅助编程工具",children:[]}]},{level:2,title:"软件测试",slug:"软件测试",link:"#软件测试",children:[]},{level:2,title:"总结",slug:"总结",link:"#总结",children:[]},{level:2,title:"附:CodeWhisperer 安装",slug:"附-codewhisperer-安装",link:"#附-codewhisperer-安装",children:[]}],path:"/daily/leverage-ai-to-boost-coding-productivity.html",pathLocale:"/",extraFields:[]},{title:"微软中国CTO演讲观后感",headers:[],path:"/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html",pathLocale:"/",extraFields:[]},{title:"对Vue不得不吐槽的事",headers:[],path:"/daily/things-I-have-to-vent-about-vue.html",pathLocale:"/",extraFields:[]},{title:"再见ChatGPT,我选择Claude2!",headers:[],path:"/daily/use-claude2-instead-of-chatgpt.html",pathLocale:"/",extraFields:[]},{title:"Vim 作者离世",headers:[],path:"/daily/vim-creator-pass-away.html",pathLocale:"/",extraFields:[]},{title:"sh与bash的区别",headers:[],path:"/daily/what-is-the-difference-between-sh-and-bash.html",pathLocale:"/",extraFields:[]},{title:"技术点评:别每张表都加tenant_id",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"正文",slug:"正文",link:"#正文",children:[]}],path:"/daily/you-dont-need-to-add-tenant_id-to-every-table.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第一册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"值得一读的文章",slug:"值得一读的文章",link:"#值得一读的文章",children:[]}],path:"/english/contemporary-college-english-1.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第二册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"值得一读的文章",slug:"值得一读的文章",link:"#值得一读的文章",children:[]}],path:"/english/contemporary-college-english-2.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第三册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"值得一读的文章",slug:"值得一读的文章",link:"#值得一读的文章",children:[]}],path:"/english/contemporary-college-english-3.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第四册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"值得一读的文章",slug:"值得一读的文章",link:"#值得一读的文章",children:[]}],path:"/english/contemporary-college-english-4.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第五册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"Who Are You and What Are You Doing Here",slug:"who-are-you-and-what-are-you-doing-here",link:"#who-are-you-and-what-are-you-doing-here",children:[]},{level:2,title:"Two Kinds",slug:"two-kinds",link:"#two-kinds",children:[]},{level:2,title:"Love is a Fallacy",slug:"love-is-a-fallacy",link:"#love-is-a-fallacy",children:[]},{level:2,title:"Rewriting American History",slug:"rewriting-american-history",link:"#rewriting-american-history",children:[]},{level:2,title:"Nobel Peace Price About Global Warming",slug:"nobel-peace-price-about-global-warming",link:"#nobel-peace-price-about-global-warming",children:[]},{level:2,title:"The Bluest Eyes",slug:"the-bluest-eyes",link:"#the-bluest-eyes",children:[]},{level:2,title:"How News Becomes Options and Opinions Off-Limits",slug:"how-news-becomes-options-and-opinions-off-limits",link:"#how-news-becomes-options-and-opinions-off-limits",children:[]},{level:2,title:"The Indispensable Opposition",slug:"the-indispensable-opposition",link:"#the-indispensable-opposition",children:[]},{level:2,title:"The Danger of a Single Story",slug:"the-danger-of-a-single-story",link:"#the-danger-of-a-single-story",children:[]},{level:2,title:"Come Rain or Come Shine",slug:"come-rain-or-come-shine",link:"#come-rain-or-come-shine",children:[]},{level:2,title:"Invisible Man",slug:"invisible-man",link:"#invisible-man",children:[]},{level:2,title:"You've Got to Find What You Love",slug:"you-ve-got-to-find-what-you-love",link:"#you-ve-got-to-find-what-you-love",children:[]},{level:2,title:"Where Do We Go from Here",slug:"where-do-we-go-from-here",link:"#where-do-we-go-from-here",children:[]}],path:"/english/contemporary-college-english-5.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第六册",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"Paper Tigers",slug:"paper-tigers",link:"#paper-tigers",children:[]},{level:2,title:"What Is News",slug:"what-is-news",link:"#what-is-news",children:[]},{level:2,title:"At War with the Planet",slug:"at-war-with-the-planet",link:"#at-war-with-the-planet",children:[]},{level:2,title:"How to Get the Poor off Our Conscience",slug:"how-to-get-the-poor-off-our-conscience",link:"#how-to-get-the-poor-off-our-conscience",children:[]},{level:2,title:"Housewifely Arts",slug:"housewifely-arts",link:"#housewifely-arts",children:[]},{level:2,title:"The One Against The Many",slug:"the-one-against-the-many",link:"#the-one-against-the-many",children:[]},{level:2,title:"Notes on the English Character",slug:"notes-on-the-english-character",link:"#notes-on-the-english-character",children:[]},{level:2,title:"The Death of a Pig",slug:"the-death-of-a-pig",link:"#the-death-of-a-pig",children:[]},{level:2,title:"Don't Eat Fortune's Cookie",slug:"don-t-eat-fortune-s-cookie",link:"#don-t-eat-fortune-s-cookie",children:[]},{level:2,title:"The Accidental Universe",slug:"the-accidental-universe",link:"#the-accidental-universe",children:[]},{level:2,title:"Rowling's Speech at Harvard",slug:"rowling-s-speech-at-harvard",link:"#rowling-s-speech-at-harvard",children:[]}],path:"/english/contemporary-college-english-6.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语1:开篇",headers:[{level:2,title:"为什么学",slug:"为什么学",link:"#为什么学",children:[]},{level:2,title:"怎么学",slug:"怎么学",link:"#怎么学",children:[{level:3,title:"建设心态",slug:"建设心态",link:"#建设心态",children:[]},{level:3,title:"提升认知",slug:"提升认知",link:"#提升认知",children:[]},{level:3,title:"明确意义",slug:"明确意义",link:"#明确意义",children:[]},{level:3,title:"制定计划",slug:"制定计划",link:"#制定计划",children:[]},{level:3,title:"培养习惯",slug:"培养习惯",link:"#培养习惯",children:[]},{level:3,title:"注重方法",slug:"注重方法",link:"#注重方法",children:[]}]}],path:"/english/everyone-can-learn-english-1-overview.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语2:音标",headers:[{level:2,title:"方法",slug:"方法",link:"#方法",children:[]},{level:2,title:"讨论",slug:"讨论",link:"#讨论",children:[{level:3,title:"建议买实体书",slug:"建议买实体书",link:"#建议买实体书",children:[]},{level:3,title:"美语是主流",slug:"美语是主流",link:"#美语是主流",children:[]},{level:3,title:"不要纠结口音",slug:"不要纠结口音",link:"#不要纠结口音",children:[]}]}],path:"/english/everyone-can-learn-english-2-pronunciation.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语3:单词",headers:[{level:2,title:"方法",slug:"方法",link:"#方法",children:[]},{level:2,title:"提醒",slug:"提醒",link:"#提醒",children:[]}],path:"/english/everyone-can-learn-english-3-words.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语4:听说",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"方法",slug:"方法",link:"#方法",children:[{level:3,title:"1.建立信心",slug:"_1-建立信心",link:"#_1-建立信心",children:[]},{level:3,title:"2.明确方向",slug:"_2-明确方向",link:"#_2-明确方向",children:[]},{level:3,title:"3.坚持输入并输出",slug:"_3-坚持输入并输出",link:"#_3-坚持输入并输出",children:[]}]}],path:"/english/everyone-can-learn-english-4-listening-and-speaking.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语5:读写",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"阅读能力升级之旅",slug:"阅读能力升级之旅",link:"#阅读能力升级之旅",children:[]},{level:2,title:"阅读方法",slug:"阅读方法",link:"#阅读方法",children:[{level:3,title:"建立信心",slug:"建立信心",link:"#建立信心",children:[]},{level:3,title:"根据蓝思值选书",slug:"根据蓝思值选书",link:"#根据蓝思值选书",children:[]},{level:3,title:"巧查生词",slug:"巧查生词",link:"#巧查生词",children:[]},{level:3,title:"阅读材料推荐",slug:"阅读材料推荐",link:"#阅读材料推荐",children:[]}]},{level:2,title:"写作",slug:"写作",link:"#写作",children:[]}],path:"/english/everyone-can-learn-english-5-reading-and-writing.html",pathLocale:"/",extraFields:[]},{title:"英文能力评测手把手教学",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"单词量",slug:"单词量",link:"#单词量",children:[]},{level:2,title:"阅读能力",slug:"阅读能力",link:"#阅读能力",children:[]}],path:"/english/how-to-self-evaluate-english-level.html",pathLocale:"/",extraFields:[]},{title:"完成刷7k单词任务",headers:[],path:"/english/learning-7000-words-task-completed.html",pathLocale:"/",extraFields:[]},{title:"让 ChatGPT 成为你的外语私教",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"准备工作",slug:"准备工作",link:"#准备工作",children:[]},{level:2,title:"常用Prompt",slug:"常用prompt",link:"#常用prompt",children:[]},{level:2,title:"进行对话",slug:"进行对话",link:"#进行对话",children:[]},{level:2,title:"记录回答",slug:"记录回答",link:"#记录回答",children:[]}],path:"/english/let-chatgpt-be-your-foreign-language-teacher.html",pathLocale:"/",extraFields:[]},{title:"旧文章精选",headers:[],path:"/frontend/old-articles.html",pathLocale:"/",extraFields:[]},{title:"前端项目性能优化实战",headers:[{level:2,title:"检测",slug:"检测",link:"#检测",children:[]},{level:2,title:"图片优化",slug:"图片优化",link:"#图片优化",children:[]},{level:2,title:"提高TTFB时间",slug:"提高ttfb时间",link:"#提高ttfb时间",children:[]},{level:2,title:"移除未使用的 Javascript",slug:"移除未使用的-javascript",link:"#移除未使用的-javascript",children:[]},{level:2,title:"延迟静态资源的加载",slug:"延迟静态资源的加载",link:"#延迟静态资源的加载",children:[]},{level:2,title:"启用文本压缩",slug:"启用文本压缩",link:"#启用文本压缩",children:[]},{level:2,title:"优化缓存策略",slug:"优化缓存策略",link:"#优化缓存策略",children:[]}],path:"/frontend/performance-optimization-in-action.html",pathLocale:"/",extraFields:[]},{title:"Git最佳实践",headers:[{level:2,title:"精简提交",slug:"精简提交",link:"#精简提交",children:[]},{level:2,title:"频繁提交",slug:"频繁提交",link:"#频繁提交",children:[]},{level:2,title:"不要提交不完整的改动",slug:"不要提交不完整的改动",link:"#不要提交不完整的改动",children:[]},{level:2,title:"提交前测试那些改动",slug:"提交前测试那些改动",link:"#提交前测试那些改动",children:[]},{level:2,title:"版本控制不是备份系统",slug:"版本控制不是备份系统",link:"#版本控制不是备份系统",children:[]},{level:2,title:"Github实例",slug:"github实例",link:"#github实例",children:[{level:3,title:"一个功能对应一个分支",slug:"一个功能对应一个分支",link:"#一个功能对应一个分支",children:[]},{level:3,title:"提交“瘦”的PR",slug:"提交-瘦-的pr",link:"#提交-瘦-的pr",children:[]},{level:3,title:"使用正确的标题",slug:"使用正确的标题",link:"#使用正确的标题",children:[]},{level:3,title:"根据模板填写PR描述",slug:"根据模板填写pr描述",link:"#根据模板填写pr描述",children:[]},{level:3,title:"自动关闭issue",slug:"自动关闭issue",link:"#自动关闭issue",children:[]},{level:3,title:"1+2 review 规则",slug:"_1-2-review-规则",link:"#_1-2-review-规则",children:[]},{level:3,title:"礼貌提问",slug:"礼貌提问",link:"#礼貌提问",children:[]}]},{level:2,title:"学习资源",slug:"学习资源",link:"#学习资源",children:[]}],path:"/git/git-best-pratices.html",pathLocale:"/",extraFields:[]},{title:"Git代码合并指南",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"功能分支合并长驻分支冲突",slug:"功能分支合并长驻分支冲突",link:"#功能分支合并长驻分支冲突",children:[{level:3,title:"解决思路",slug:"解决思路",link:"#解决思路",children:[]},{level:3,title:"操作步骤",slug:"操作步骤",link:"#操作步骤",children:[]}]},{level:2,title:"功能分支被污染",slug:"功能分支被污染",link:"#功能分支被污染",children:[{level:3,title:"解决思路",slug:"解决思路-1",link:"#解决思路-1",children:[]},{level:3,title:"操作步骤",slug:"操作步骤-1",link:"#操作步骤-1",children:[]}]},{level:2,title:"挑选别的分支部分代码合并",slug:"挑选别的分支部分代码合并",link:"#挑选别的分支部分代码合并",children:[{level:3,title:"解决思路",slug:"解决思路-2",link:"#解决思路-2",children:[]},{level:3,title:"操作步骤",slug:"操作步骤-2",link:"#操作步骤-2",children:[]}]}],path:"/git/git-definitive-guide-to-merge-code.html",pathLocale:"/",extraFields:[]},{title:"Git查看历史记录小技巧",headers:[],path:"/git/git-history-two-tricks-in-idea.html",pathLocale:"/",extraFields:[]},{title:"Git常用命令",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"配置",slug:"配置",link:"#配置",children:[]},{level:2,title:"增强",slug:"增强",link:"#增强",children:[]},{level:2,title:"记住账号密码",slug:"记住账号密码",link:"#记住账号密码",children:[]},{level:2,title:"初始化",slug:"初始化",link:"#初始化",children:[]},{level:2,title:"本地提交",slug:"本地提交",link:"#本地提交",children:[{level:3,title:"取消未暂存的修改",slug:"取消未暂存的修改",link:"#取消未暂存的修改",children:[]},{level:3,title:"取消add",slug:"取消add",link:"#取消add",children:[]},{level:3,title:"取消提交",slug:"取消提交",link:"#取消提交",children:[]},{level:3,title:"修正提交",slug:"修正提交",link:"#修正提交",children:[]},{level:3,title:"stash修改",slug:"stash修改",link:"#stash修改",children:[]},{level:3,title:"恢复stash",slug:"恢复stash",link:"#恢复stash",children:[]}]},{level:2,title:"分支管理",slug:"分支管理",link:"#分支管理",children:[{level:3,title:"创建分支",slug:"创建分支",link:"#创建分支",children:[]},{level:3,title:"查看远程分支",slug:"查看远程分支",link:"#查看远程分支",children:[]},{level:3,title:"创建干净历史分支",slug:"创建干净历史分支",link:"#创建干净历史分支",children:[]},{level:3,title:"删除分支",slug:"删除分支",link:"#删除分支",children:[]}]},{level:2,title:"远程仓库",slug:"远程仓库",link:"#远程仓库",children:[{level:3,title:"远程仓库管理",slug:"远程仓库管理",link:"#远程仓库管理",children:[]},{level:3,title:"浅克隆",slug:"浅克隆",link:"#浅克隆",children:[]},{level:3,title:"克隆指定分支",slug:"克隆指定分支",link:"#克隆指定分支",children:[]},{level:3,title:"克隆失败因为文件名太长",slug:"克隆失败因为文件名太长",link:"#克隆失败因为文件名太长",children:[]},{level:3,title:"强行推送",slug:"强行推送",link:"#强行推送",children:[]},{level:3,title:"取消错误的推送",slug:"取消错误的推送",link:"#取消错误的推送",children:[]}]},{level:2,title:"标签管理",slug:"标签管理",link:"#标签管理",children:[{level:3,title:"新建本地标签",slug:"新建本地标签",link:"#新建本地标签",children:[]},{level:3,title:"删除本地标签",slug:"删除本地标签",link:"#删除本地标签",children:[]},{level:3,title:"查看本地所有标签",slug:"查看本地所有标签",link:"#查看本地所有标签",children:[]},{level:3,title:"推送本地标签",slug:"推送本地标签",link:"#推送本地标签",children:[]},{level:3,title:"获取远程标签",slug:"获取远程标签",link:"#获取远程标签",children:[]},{level:3,title:"删除远程标签",slug:"删除远程标签",link:"#删除远程标签",children:[]}]},{level:2,title:"其他",slug:"其他",link:"#其他",children:[{level:3,title:"cherry-pick",slug:"cherry-pick",link:"#cherry-pick",children:[]},{level:3,title:"merge unrelated histories",slug:"merge-unrelated-histories",link:"#merge-unrelated-histories",children:[]},{level:3,title:"git log 丢失最新提交",slug:"git-log-丢失最新提交",link:"#git-log-丢失最新提交",children:[]},{level:3,title:"查看分支创建时间",slug:"查看分支创建时间",link:"#查看分支创建时间",children:[]},{level:3,title:"根据文件搜索历史",slug:"根据文件搜索历史",link:"#根据文件搜索历史",children:[]},{level:3,title:"从所有提交中删除一个文件",slug:"从所有提交中删除一个文件",link:"#从所有提交中删除一个文件",children:[]}]}],path:"/git/git-useful-commands.html",pathLocale:"/",extraFields:[]},{title:"GitLab CI",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"安装与配置",slug:"安装与配置",link:"#安装与配置",children:[{level:3,title:"GitLab Runner 安装",slug:"gitlab-runner-安装",link:"#gitlab-runner-安装",children:[]},{level:3,title:"GitLab Runner 注册",slug:"gitlab-runner-注册",link:"#gitlab-runner-注册",children:[]},{level:3,title:"提交.gitlab-ci.yml",slug:"提交-gitlab-ci-yml",link:"#提交-gitlab-ci-yml",children:[]}]},{level:2,title:"合并代码前进行检查",slug:"合并代码前进行检查",link:"#合并代码前进行检查",children:[{level:3,title:"背景",slug:"背景",link:"#背景",children:[]},{level:3,title:"设置MR检查",slug:"设置mr检查",link:"#设置mr检查",children:[]},{level:3,title:".gitlab-ci.yml 示例",slug:"gitlab-ci-yml-示例",link:"#gitlab-ci-yml-示例",children:[]},{level:3,title:"效果",slug:"效果",link:"#效果",children:[]}]},{level:2,title:"集成单元测试",slug:"集成单元测试",link:"#集成单元测试",children:[]},{level:2,title:"线上发布 jar",slug:"线上发布-jar",link:"#线上发布-jar",children:[{level:3,title:"Maven配置",slug:"maven配置",link:"#maven配置",children:[]},{level:3,title:".gitlab-ci.yml 配置",slug:"gitlab-ci-yml-配置",link:"#gitlab-ci-yml-配置",children:[]},{level:3,title:"拉取最新的jar",slug:"拉取最新的jar",link:"#拉取最新的jar",children:[]}]},{level:2,title:"保存中间产物",slug:"保存中间产物",link:"#保存中间产物",children:[]},{level:2,title:"其他问题与解决方案",slug:"其他问题与解决方案",link:"#其他问题与解决方案",children:[{level:3,title:"Node.js",slug:"node-js",link:"#node-js",children:[]},{level:3,title:"创建不了容器",slug:"创建不了容器",link:"#创建不了容器",children:[]},{level:3,title:"本地成功,流水线失败",slug:"本地成功-流水线失败",link:"#本地成功-流水线失败",children:[]}]},{level:2,title:"参考文档",slug:"参考文档",link:"#参考文档",children:[]}],path:"/git/gitlab-ci.html",pathLocale:"/",extraFields:[]},{title:"再论Git Flow",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"动机",slug:"动机",link:"#动机",children:[]},{level:2,title:"分析",slug:"分析",link:"#分析",children:[{level:3,title:"剔除代码",slug:"剔除代码",link:"#剔除代码",children:[]},{level:3,title:"再次提交",slug:"再次提交",link:"#再次提交",children:[]},{level:3,title:"比较优劣",slug:"比较优劣",link:"#比较优劣",children:[]}]},{level:2,title:"实例",slug:"实例",link:"#实例",children:[{level:3,title:"分支模型",slug:"分支模型",link:"#分支模型",children:[]},{level:3,title:"功能提交",slug:"功能提交",link:"#功能提交",children:[]},{level:3,title:"功能回撤",slug:"功能回撤",link:"#功能回撤",children:[]}]},{level:2,title:"结论",slug:"结论",link:"#结论",children:[]}],path:"/git/rethinking-git-flow.html",pathLocale:"/",extraFields:[]},{title:"操作 Gitlab MR 的命令行工具",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[{level:3,title:"解压zip",slug:"解压zip",link:"#解压zip",children:[]},{level:3,title:"安装git bash",slug:"安装git-bash",link:"#安装git-bash",children:[]}]},{level:2,title:"配置",slug:"配置",link:"#配置",children:[{level:3,title:"gitlab_token",slug:"gitlab-token",link:"#gitlab-token",children:[]},{level:3,title:"codebases",slug:"codebases",link:"#codebases",children:[]},{level:3,title:"环境变量",slug:"环境变量",link:"#环境变量",children:[]},{level:3,title:"IDEA",slug:"idea",link:"#idea",children:[]}]},{level:2,title:"使用",slug:"使用",link:"#使用",children:[{level:3,title:"创建MR",slug:"创建mr",link:"#创建mr",children:[]},{level:3,title:"查看MR",slug:"查看mr",link:"#查看mr",children:[]},{level:3,title:"合并MR",slug:"合并mr",link:"#合并mr",children:[]},{level:3,title:"冲突处理",slug:"冲突处理",link:"#冲突处理",children:[]}]}],path:"/git/use-command-line-tool-to-manage-gitlab-merge-request.html",pathLocale:"/",extraFields:[]},{title:"IDEA常见问题与解决方案",headers:[{level:2,title:"启动参数过长",slug:"启动参数过长",link:"#启动参数过长",children:[]},{level:2,title:"设置JDK版本",slug:"设置jdk版本",link:"#设置jdk版本",children:[]},{level:2,title:"lombok 编译报错",slug:"lombok-编译报错",link:"#lombok-编译报错",children:[]},{level:2,title:"设置启动参数",slug:"设置启动参数",link:"#设置启动参数",children:[]},{level:2,title:"栈溢出",slug:"栈溢出",link:"#栈溢出",children:[]},{level:2,title:"内存不足",slug:"内存不足",link:"#内存不足",children:[]},{level:2,title:"热加载",slug:"热加载",link:"#热加载",children:[]},{level:2,title:"终端加载环境变量",slug:"终端加载环境变量",link:"#终端加载环境变量",children:[]},{level:2,title:"添加外部jar作为依赖",slug:"添加外部jar作为依赖",link:"#添加外部jar作为依赖",children:[]},{level:2,title:"文件找不到——依赖冲突",slug:"文件找不到——依赖冲突",link:"#文件找不到——依赖冲突",children:[]},{level:2,title:"自动import",slug:"自动import",link:"#自动import",children:[]},{level:2,title:"文件乱码",slug:"文件乱码",link:"#文件乱码",children:[]},{level:2,title:"autowired 提示变量未赋值",slug:"autowired-提示变量未赋值",link:"#autowired-提示变量未赋值",children:[]}],path:"/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html",pathLocale:"/",extraFields:[]},{title:"Maven常见问题与解决方案",headers:[{level:2,title:"运行 class 找不到主类",slug:"运行-class-找不到主类",link:"#运行-class-找不到主类",children:[]},{level:2,title:"运行 jar 找不到主类",slug:"运行-jar-找不到主类",link:"#运行-jar-找不到主类",children:[]},{level:2,title:"编译时找不到主类",slug:"编译时找不到主类",link:"#编译时找不到主类",children:[]},{level:2,title:"设置Maven目录",slug:"设置maven目录",link:"#设置maven目录",children:[]},{level:2,title:"无法识别 Maven 项目",slug:"无法识别-maven-项目",link:"#无法识别-maven-项目",children:[]},{level:2,title:"使用了不想要的镜像源",slug:"使用了不想要的镜像源",link:"#使用了不想要的镜像源",children:[]},{level:2,title:"下载 jar 失败",slug:"下载-jar-失败",link:"#下载-jar-失败",children:[]},{level:2,title:"私服认证401",slug:"私服认证401",link:"#私服认证401",children:[]},{level:2,title:"避免缓存",slug:"避免缓存",link:"#避免缓存",children:[]},{level:2,title:"参考资料",slug:"参考资料",link:"#参考资料",children:[]}],path:"/java/Resolving-Common-Problems-in-Maven.md.html",pathLocale:"/",extraFields:[]},{title:"避免密码明文传输",headers:[{level:2,title:"说明",slug:"说明",link:"#说明",children:[]},{level:2,title:"前端代码",slug:"前端代码",link:"#前端代码",children:[]},{level:2,title:"后端代码",slug:"后端代码",link:"#后端代码",children:[]}],path:"/java/avoid-sending-password-in-plaintext.html",pathLocale:"/",extraFields:[]},{title:"检查名字是否重复",headers:[{level:2,title:"推荐做法",slug:"推荐做法",link:"#推荐做法",children:[]},{level:2,title:"Why",slug:"why",link:"#why",children:[]}],path:"/java/check-if-name-exists.html",pathLocale:"/",extraFields:[]},{title:"Excel处理常用实践",headers:[{level:2,title:"基础知识",slug:"基础知识",link:"#基础知识",children:[]},{level:2,title:"应用框架",slug:"应用框架",link:"#应用框架",children:[{level:3,title:"导出",slug:"导出",link:"#导出",children:[]},{level:3,title:"导入",slug:"导入",link:"#导入",children:[]}]},{level:2,title:"常见问题与解决方案",slug:"常见问题与解决方案",link:"#常见问题与解决方案",children:[{level:3,title:"浏览器下载",slug:"浏览器下载",link:"#浏览器下载",children:[]},{level:3,title:"上传文件大小限制",slug:"上传文件大小限制",link:"#上传文件大小限制",children:[]},{level:3,title:"缺少字体",slug:"缺少字体",link:"#缺少字体",children:[]},{level:3,title:"序列化失败",slug:"序列化失败",link:"#序列化失败",children:[]}]}],path:"/java/common-practices-for-handling-excel.html",pathLocale:"/",extraFields:[]},{title:"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"下载",slug:"下载",link:"#下载",children:[]},{level:2,title:"修改",slug:"修改",link:"#修改",children:[]},{level:2,title:"上传",slug:"上传",link:"#上传",children:[]}],path:"/java/how-to-convert-snapshot-into-release-jar-without-source-code.html",pathLocale:"/",extraFields:[]},{title:"根据时间范围查询推荐实践",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"需求",slug:"需求",link:"#需求",children:[]},{level:2,title:"分析",slug:"分析",link:"#分析",children:[]},{level:2,title:"实现",slug:"实现",link:"#实现",children:[{level:3,title:"MySQL",slug:"mysql",link:"#mysql",children:[]},{level:3,title:"MyBatis",slug:"mybatis",link:"#mybatis",children:[]},{level:3,title:"LoxalDate",slug:"loxaldate",link:"#loxaldate",children:[]},{level:3,title:"Jackson",slug:"jackson",link:"#jackson",children:[]}]},{level:2,title:"结语",slug:"结语",link:"#结语",children:[]}],path:"/java/recommend-practices-for-query-by-date-range.html",pathLocale:"/",extraFields:[]},{title:"Jackson 经典异常 UnrecognizedPropertyException",headers:[],path:"/java/why-i-prefer-fastjson-instead-of-jackson.html",pathLocale:"/",extraFields:[]},{title:"升个jar版本,怎么这么难?",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"接口调用示意图",slug:"接口调用示意图",link:"#接口调用示意图",children:[]},{level:2,title:"已知信息",slug:"已知信息",link:"#已知信息",children:[]},{level:2,title:"问题",slug:"问题",link:"#问题",children:[]},{level:2,title:"复盘",slug:"复盘",link:"#复盘",children:[]}],path:"/java/why-is-it-so-hard-to-upgrade-dependencies.html",pathLocale:"/",extraFields:[]},{title:"数据备份案例:mysqldump实战",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"架构",slug:"架构",link:"#架构",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[]},{level:2,title:"导出",slug:"导出",link:"#导出",children:[{level:3,title:"--set-gtid-purged=OFF",slug:"set-gtid-purged-off",link:"#set-gtid-purged-off",children:[]},{level:3,title:"--ignore-table",slug:"ignore-table",link:"#ignore-table",children:[]}]},{level:2,title:"导入",slug:"导入",link:"#导入",children:[]},{level:2,title:"为什么不?",slug:"为什么不",link:"#为什么不",children:[]}],path:"/mysql/mysql-backup-case-study-mysqldump-in-action.html",pathLocale:"/",extraFields:[]},{title:"数据迁移案例:表AUTO_INCREMENT加10w",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"备份",slug:"备份",link:"#备份",children:[]},{level:2,title:"SQL编写",slug:"sql编写",link:"#sql编写",children:[]},{level:2,title:"存储过程(可复用",slug:"存储过程-可复用",link:"#存储过程-可复用",children:[]}],path:"/mysql/mysql-data-migration-case-study-add-auto-increment.html",pathLocale:"/",extraFields:[]},{title:"MySQL 命令行执行SQL的细节",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"环境说明",slug:"环境说明",link:"#环境说明",children:[]},{level:2,title:"执行SQL文件",slug:"执行sql文件",link:"#执行sql文件",children:[]},{level:2,title:"复制粘贴执行",slug:"复制粘贴执行",link:"#复制粘贴执行",children:[]},{level:2,title:"如果要删除错误的数据怎么办?",slug:"如果要删除错误的数据怎么办",link:"#如果要删除错误的数据怎么办",children:[]}],path:"/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html",pathLocale:"/",extraFields:[]},{title:"Python 导出 MySQL 库表信息到 Excel",headers:[{level:2,title:"需求",slug:"需求",link:"#需求",children:[]},{level:2,title:"代码",slug:"代码",link:"#代码",children:[]},{level:2,title:"其他细节",slug:"其他细节",link:"#其他细节",children:[]}],path:"/python/export-mysql-table-into-excel.html",pathLocale:"/",extraFields:[]},{title:"单元测试概述",headers:[{level:2,title:"Why",slug:"why",link:"#why",children:[]},{level:2,title:"What",slug:"what",link:"#what",children:[]},{level:2,title:"How",slug:"how",link:"#how",children:[{level:3,title:"测试代码的风格",slug:"测试代码的风格",link:"#测试代码的风格",children:[]},{level:3,title:"测试难点",slug:"测试难点",link:"#测试难点",children:[]},{level:3,title:"常用工具",slug:"常用工具",link:"#常用工具",children:[]}]},{level:2,title:"Bad Examples",slug:"bad-examples",link:"#bad-examples",children:[{level:3,title:"没有测试类",slug:"没有测试类",link:"#没有测试类",children:[]},{level:3,title:"没有断言",slug:"没有断言",link:"#没有断言",children:[]},{level:3,title:"无法重复执行",slug:"无法重复执行",link:"#无法重复执行",children:[]}]}],path:"/software-testing/unit-testing-overview.html",pathLocale:"/",extraFields:[]},{title:"使用 RestAssured 进行 API 测试",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"What",slug:"what",link:"#what",children:[]},{level:2,title:"Why",slug:"why",link:"#why",children:[]},{level:2,title:"为什么不用Postman",slug:"为什么不用postman",link:"#为什么不用postman",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[]},{level:2,title:"快速上手",slug:"快速上手",link:"#快速上手",children:[]},{level:2,title:"通用设置",slug:"通用设置",link:"#通用设置",children:[]},{level:2,title:"请求示例",slug:"请求示例",link:"#请求示例",children:[]},{level:2,title:"接口依赖",slug:"接口依赖",link:"#接口依赖",children:[]},{level:2,title:"上传示例",slug:"上传示例",link:"#上传示例",children:[]},{level:2,title:"下载示例",slug:"下载示例",link:"#下载示例",children:[]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[]},{level:2,title:"其他问题",slug:"其他问题",link:"#其他问题",children:[{level:3,title:"为什么不用 Pytest",slug:"为什么不用-pytest",link:"#为什么不用-pytest",children:[]},{level:3,title:"这也是单元测试吗",slug:"这也是单元测试吗",link:"#这也是单元测试吗",children:[]}]},{level:2,title:"参考资料",slug:"参考资料",link:"#参考资料",children:[]}],path:"/software-testing/use-RestAssured-for-api-testing.html",pathLocale:"/",extraFields:[]},{title:"使用 Cypress 进行端对端测试",headers:[{level:2,title:"为什么写端对端测试",slug:"为什么写端对端测试",link:"#为什么写端对端测试",children:[]},{level:2,title:"为什么用 Cypress",slug:"为什么用-cypress",link:"#为什么用-cypress",children:[]},{level:2,title:"快速开始",slug:"快速开始",link:"#快速开始",children:[{level:3,title:"安装",slug:"安装",link:"#安装",children:[]},{level:3,title:"加速下载",slug:"加速下载",link:"#加速下载",children:[]},{level:3,title:"目录结构",slug:"目录结构",link:"#目录结构",children:[]},{level:3,title:"与 Jest 协同工作",slug:"与-jest-协同工作",link:"#与-jest-协同工作",children:[]},{level:3,title:"检查依赖及生产安装依赖命令",slug:"检查依赖及生产安装依赖命令",link:"#检查依赖及生产安装依赖命令",children:[]},{level:3,title:"第一个用例",slug:"第一个用例",link:"#第一个用例",children:[]},{level:3,title:"更复杂的示例",slug:"更复杂的示例",link:"#更复杂的示例",children:[]}]},{level:2,title:"结合TypeScript",slug:"结合typescript",link:"#结合typescript",children:[]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[{level:3,title:"直接运行 Cypress",slug:"直接运行-cypress",link:"#直接运行-cypress",children:[]},{level:3,title:"使用 start-server-and-test",slug:"使用-start-server-and-test",link:"#使用-start-server-and-test",children:[]},{level:3,title:"This job is stuck",slug:"this-job-is-stuck",link:"#this-job-is-stuck",children:[]},{level:3,title:"Cypress Dashbord",slug:"cypress-dashbord",link:"#cypress-dashbord",children:[]}]},{level:2,title:"总结",slug:"总结",link:"#总结",children:[]},{level:2,title:"拓展阅读",slug:"拓展阅读",link:"#拓展阅读",children:[]}],path:"/software-testing/use-cypress-for-e2e-testing.html",pathLocale:"/",extraFields:[]},{title:"使用 Jest 实践测试驱动开发",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"环境搭建",slug:"环境搭建",link:"#环境搭建",children:[]},{level:2,title:"开发",slug:"开发",link:"#开发",children:[{level:3,title:"文件初始化",slug:"文件初始化",link:"#文件初始化",children:[]},{level:3,title:"第一个用例",slug:"第一个用例",link:"#第一个用例",children:[]},{level:3,title:"第二个用例",slug:"第二个用例",link:"#第二个用例",children:[]},{level:3,title:"第三个用例",slug:"第三个用例",link:"#第三个用例",children:[]},{level:3,title:"第四个用例",slug:"第四个用例",link:"#第四个用例",children:[]},{level:3,title:"第五个用例",slug:"第五个用例",link:"#第五个用例",children:[]},{level:3,title:"重构",slug:"重构",link:"#重构",children:[]}]},{level:2,title:"结论",slug:"结论",link:"#结论",children:[]}],path:"/software-testing/use-jest-for-test-driven-development.html",pathLocale:"/",extraFields:[]},{title:"下一代 UI 自动化测试工具 Playwright",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[]},{level:2,title:"使用",slug:"使用",link:"#使用",children:[{level:3,title:"代码生成",slug:"代码生成",link:"#代码生成",children:[]},{level:3,title:"修改代码",slug:"修改代码",link:"#修改代码",children:[]},{level:3,title:"执行用例",slug:"执行用例",link:"#执行用例",children:[]},{level:3,title:"调试用例",slug:"调试用例",link:"#调试用例",children:[]},{level:3,title:"查看报告",slug:"查看报告",link:"#查看报告",children:[]}]},{level:2,title:"常见场景与解决方案",slug:"常见场景与解决方案",link:"#常见场景与解决方案",children:[{level:3,title:"应用登录",slug:"应用登录",link:"#应用登录",children:[]},{level:3,title:"环境变量",slug:"环境变量",link:"#环境变量",children:[]},{level:3,title:"超时时间",slug:"超时时间",link:"#超时时间",children:[]},{level:3,title:"元素选择",slug:"元素选择",link:"#元素选择",children:[]},{level:3,title:"声明断言 && 检查元素是否存在",slug:"声明断言-检查元素是否存在",link:"#声明断言-检查元素是否存在",children:[]},{level:3,title:"获取第n个元素",slug:"获取第n个元素",link:"#获取第n个元素",children:[]},{level:3,title:"遍历元素",slug:"遍历元素",link:"#遍历元素",children:[]},{level:3,title:"获取元素属性",slug:"获取元素属性",link:"#获取元素属性",children:[]},{level:3,title:"判断子元素数量",slug:"判断子元素数量",link:"#判断子元素数量",children:[]},{level:3,title:"鼠标悬浮",slug:"鼠标悬浮",link:"#鼠标悬浮",children:[]},{level:3,title:"操作剪贴板",slug:"操作剪贴板",link:"#操作剪贴板",children:[]}]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[]},{level:2,title:"其他",slug:"其他",link:"#其他",children:[{level:3,title:"setup.py bdist_wheel did not run successfully",slug:"setup-py-bdist-wheel-did-not-run-successfully",link:"#setup-py-bdist-wheel-did-not-run-successfully",children:[]}]}],path:"/software-testing/use-playwright-for-ui-testing.html",pathLocale:"/",extraFields:[]},{title:"使用 Postman 进行 API 测试",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"本地调试",slug:"本地调试",link:"#本地调试",children:[{level:3,title:"接口集合",slug:"接口集合",link:"#接口集合",children:[]},{level:3,title:"从 cURL 导入",slug:"从-curl-导入",link:"#从-curl-导入",children:[]},{level:3,title:"环境变量",slug:"环境变量",link:"#环境变量",children:[]},{level:3,title:"请求设置",slug:"请求设置",link:"#请求设置",children:[]}]},{level:2,title:"接口测试",slug:"接口测试",link:"#接口测试",children:[{level:3,title:"编写用例",slug:"编写用例",link:"#编写用例",children:[]},{level:3,title:"上传文件",slug:"上传文件",link:"#上传文件",children:[]},{level:3,title:"运行集合",slug:"运行集合",link:"#运行集合",children:[]}]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[{level:3,title:"导出接口",slug:"导出接口",link:"#导出接口",children:[]},{level:3,title:"导出环境变量",slug:"导出环境变量",link:"#导出环境变量",children:[]},{level:3,title:"复制要上传的文件",slug:"复制要上传的文件",link:"#复制要上传的文件",children:[]},{level:3,title:"提交到Git",slug:"提交到git",link:"#提交到git",children:[]},{level:3,title:"建立CI任务",slug:"建立ci任务",link:"#建立ci任务",children:[]}]}],path:"/software-testing/use-postman-for-api-testing.html",pathLocale:"/",extraFields:[]},{title:"科学上网",headers:[{level:2,title:"说明",slug:"说明",link:"#说明",children:[]},{level:2,title:"购买指南",slug:"购买指南",link:"#购买指南",children:[]},{level:2,title:"客户端",slug:"客户端",link:"#客户端",children:[]},{level:2,title:"导入配置",slug:"导入配置",link:"#导入配置",children:[]},{level:2,title:"其他",slug:"其他",link:"#其他",children:[{level:3,title:"500 内部代理错误",slug:"_500-内部代理错误",link:"#_500-内部代理错误",children:[]},{level:3,title:"修改PAC文件",slug:"修改pac文件",link:"#修改pac文件",children:[]},{level:3,title:"使用 New Bing",slug:"使用-new-bing",link:"#使用-new-bing",children:[]},{level:3,title:"申请美区apple id",slug:"申请美区apple-id",link:"#申请美区apple-id",children:[]}]}],path:"/tools/how-to-connect-to-internet.html",pathLocale:"/",extraFields:[]},{title:"",headers:[],path:"/404.html",pathLocale:"/",extraFields:[]},{title:"Daily",headers:[],path:"/daily/",pathLocale:"/",extraFields:[]},{title:"English",headers:[],path:"/english/",pathLocale:"/",extraFields:[]},{title:"Frontend",headers:[],path:"/frontend/",pathLocale:"/",extraFields:[]},{title:"Git",headers:[],path:"/git/",pathLocale:"/",extraFields:[]},{title:"Java",headers:[],path:"/java/",pathLocale:"/",extraFields:[]},{title:"Mysql",headers:[],path:"/mysql/",pathLocale:"/",extraFields:[]},{title:"Python",headers:[],path:"/python/",pathLocale:"/",extraFields:[]},{title:"Software Testing",headers:[],path:"/software-testing/",pathLocale:"/",extraFields:[]},{title:"Tools",headers:[],path:"/tools/",pathLocale:"/",extraFields:[]},{title:"分类",headers:[],path:"/category/",pathLocale:"/",extraFields:[]},{title:"标签",headers:[],path:"/tag/",pathLocale:"/",extraFields:[]},{title:"文章",headers:[],path:"/article/",pathLocale:"/",extraFields:[]},{title:"收藏",headers:[],path:"/star/",pathLocale:"/",extraFields:[]},{title:"时间轴",headers:[],path:"/timeline/",pathLocale:"/",extraFields:[]},{title:"标签: Daily",headers:[],path:"/tag/daily/",pathLocale:"/",extraFields:[]},{title:"标签: Frontend",headers:[],path:"/tag/frontend/",pathLocale:"/",extraFields:[]},{title:"标签: Video",headers:[],path:"/tag/video/",pathLocale:"/",extraFields:[]},{title:"标签: Linux",headers:[],path:"/tag/linux/",pathLocale:"/",extraFields:[]},{title:"标签: Docker",headers:[],path:"/tag/docker/",pathLocale:"/",extraFields:[]},{title:"标签: MySQL",headers:[],path:"/tag/mysql/",pathLocale:"/",extraFields:[]},{title:"标签: AI",headers:[],path:"/tag/ai/",pathLocale:"/",extraFields:[]},{title:"标签: Emotion",headers:[],path:"/tag/emotion/",pathLocale:"/",extraFields:[]},{title:"标签: Working Experience",headers:[],path:"/tag/working-experience/",pathLocale:"/",extraFields:[]},{title:"标签: Tool",headers:[],path:"/tag/tool/",pathLocale:"/",extraFields:[]},{title:"标签: Design",headers:[],path:"/tag/design/",pathLocale:"/",extraFields:[]},{title:"标签: English",headers:[],path:"/tag/english/",pathLocale:"/",extraFields:[]},{title:"标签: Git",headers:[],path:"/tag/git/",pathLocale:"/",extraFields:[]},{title:"标签: GitLab",headers:[],path:"/tag/gitlab/",pathLocale:"/",extraFields:[]},{title:"标签: Java",headers:[],path:"/tag/java/",pathLocale:"/",extraFields:[]},{title:"标签: Node.js",headers:[],path:"/tag/node.js/",pathLocale:"/",extraFields:[]},{title:"标签: Python",headers:[],path:"/tag/python/",pathLocale:"/",extraFields:[]},{title:"标签: JavaScript",headers:[],path:"/tag/javascript/",pathLocale:"/",extraFields:[]},{title:"标签: Testing",headers:[],path:"/tag/testing/",pathLocale:"/",extraFields:[]}],V2=W(M2),F2=()=>V2,N2=({searchIndex:e,routeLocale:t,query:l,maxSuggestions:n})=>{const a=E(()=>e.value.filter(i=>i.pathLocale===t.value));return E(()=>{const i=l.value.trim().toLowerCase();if(!i)return[];const r=[],o=(c,u)=>{Ls(i,[u.title])&&r.push({link:`${c.path}#${u.slug}`,title:c.title,header:u.title});for(const d of u.children){if(r.length>=n.value)return;o(c,d)}};for(const c of a.value){if(r.length>=n.value)break;if(Ls(i,[c.title,...c.extraFields])){r.push({link:c.path,title:c.title});continue}for(const u of c.headers){if(r.length>=n.value)break;o(c,u)}}return r})},j2=e=>{const t=W(0);return{focusIndex:t,focusNext:()=>{t.value{t.value>0?t.value-=1:t.value=e.value.length-1}}},B2=M({name:"SearchBox",props:{locales:{type:Object,required:!1,default:()=>({})},hotKeys:{type:Array,required:!1,default:()=>[]},maxSuggestions:{type:Number,required:!1,default:5}},setup(e){const{locales:t,hotKeys:l,maxSuggestions:n}=Sd(e),a=Ve(),i=mt(),r=F2(),o=W(null),c=W(!1),u=W(""),d=E(()=>t.value[i.value]??{}),p=N2({searchIndex:r,routeLocale:i,query:u,maxSuggestions:n}),{focusIndex:h,focusNext:f,focusPrev:b}=j2(p);$2({input:o,hotKeys:l});const w=E(()=>c.value&&!!p.value.length),x=()=>{w.value&&b()},g=()=>{w.value&&f()},_=P=>{if(!w.value)return;const O=p.value[P];O&&a.push(O.link).then(()=>{u.value="",h.value=0})};return()=>s("form",{class:"search-box",role:"search"},[s("input",{ref:o,type:"search",placeholder:d.value.placeholder,autocomplete:"off",spellcheck:!1,value:u.value,onFocus:()=>c.value=!0,onBlur:()=>c.value=!1,onInput:P=>u.value=P.target.value,onKeydown:P=>{switch(P.key){case"ArrowUp":{x();break}case"ArrowDown":{g();break}case"Enter":{P.preventDefault(),_(h.value);break}}}}),w.value&&s("ul",{class:"suggestions",onMouseleave:()=>h.value=-1},p.value.map(({link:P,title:O,header:B},C)=>s("li",{class:["suggestion",{focus:h.value===C}],onMouseenter:()=>h.value=C,onMousedown:()=>_(C)},s("a",{href:P,onClick:V=>V.preventDefault()},[s("span",{class:"page-title"},O),B&&s("span",{class:"page-header"},`> ${B}`)]))))])}});const z2={},H2=["s","/"],q2=5,U2=dt({enhance({app:e}){e.component("SearchBox",t=>s(B2,{locales:z2,hotKeys:H2,maxSuggestions:q2,...t}))}}),Cn=[N0,Zh,sf,pf,vf,bf,xf,Cf,$f,Uf,O2,U2],G2=[["v-8daa1a0e","/",{y:"h",t:"levy's blog",i:"home",O:null},["/README.md"]],["v-22a39d25","/about.html",{y:"p",t:"关于",i:"circle-info",O:null},[":md"]],["v-30a50b00","/daily/a-vuepress2-entertaining-video.html",{d:1690848e6,l:"2023年8月1日",g:["Daily","Frontend","Video"],e:`

VuePress2 娱乐视频

-

参考《原神,启动》的梗,做的一个娱乐向视频。

-`,r:{minutes:.11,words:34},y:"a",t:"VuePress2 娱乐视频",O:-1690848e6},[":md"]],["v-0481cc80","/daily/a-warning-from-navicat.html",{d:16907616e5,l:"2023年7月31日",g:["Daily","Video"],e:`

来自Navicat的侵权警告

-

公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。

-

另外,开发者常用的软件的合法替代品,视频中也有推荐。

-`,r:{minutes:.27,words:81},y:"a",t:"来自Navicat的侵权警告",O:-16907616e5},[":md"]],["v-31eac486","/daily/about-arm-things-you-need-to-know.html",{d:16909344e5,l:"2023年8月2日",g:["Daily","Linux","Docker"],e:`

关于 Arm 你需要了解的三件事

-

Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。

-

跟我们有什么关系呢?

-
    -
  1. MacOS 的 M1 芯片是基于 Arm 的
  2. -
  3. 云厂商及生态都在积极与 Arm 进行合作
  4. -
  5. Docker 镜像的构建有注意事项
  6. -
-`,r:{minutes:.31,words:92},y:"a",t:"关于 Arm 你需要了解的三件事",O:-16909344e5},[":md"]],["v-79e139f8","/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html",{d:16920576e5,l:"2023年8月15日",g:["Daily","Video","MySQL"],e:`

Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

-

Background

-

Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci?

-
CREATE TABLE \`my_table\` (
-  \`id\` bigint NOT NULL AUTO_INCREMENT,
-  PRIMARY KEY (\`id\`) USING BTREE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-

That is the knowledge that today I want to share with you.

-`,r:{minutes:1.78,words:533},y:"a",t:"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?",O:-16920576e5},[":md"]],["v-7a74360a","/daily/claude-ai-in-action-extract-info-from-html.html",{d:16929216e5,l:"2023年8月25日",g:["Daily","Video","AI"],e:`

Claude AI应用案例,从HTML中抽取文本

-

今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!

-`,r:{minutes:.19,words:58},y:"a",t:"Claude AI应用案例,从HTML中抽取文本",O:-16929216e5},[":md"]],["v-76f251d2","/daily/copy-code-may-not-be-guilty.html",{d:16955136e5,l:"2023年9月24日",g:["Daily"],e:`

复制代码也许不是罪

-

前言

-

熟悉我的人都知道,我对代码是有追求的。

-

正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。

-

我早期认为:复制代码就是菜。

-

后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

-

而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

-

编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

-`,r:{minutes:1.51,words:453},y:"a",t:"复制代码也许不是罪",O:-16955136e5},[":md"]],["v-f47f129e","/daily/dont-try-to-argue-with-a-sb.html",{d:16911072e5,l:"2023年8月4日",g:["Daily","Emotion","Working Experience","Video"],e:`

不要与傻逼进行争吵

-

这是个职场吐槽帖,也是个自我反思、情绪管理帖。

-

我在沟通上,有以下可以改正的点:
-0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你

-
    -
  1. 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你
  2. -
  3. 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通
  4. -
  5. 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答
  6. -
  7. 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!
  8. -
-`,r:{minutes:3.92,words:1176},y:"a",t:"不要与傻逼进行争吵",O:-16911072e5},[":md"]],["v-fd7b7f92","/daily/iteration-retrospective-of-sanyuan.html",{d:16941312e5,l:"2023年9月8日",g:["Daily","Working Experience"],e:`

迭代复盘之三员管理

-

前言

-

本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。

-

这次迭代因为各种原因,延期了快一个星期(周六还加了班)。

-

那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。

-`,r:{minutes:2.52,words:756},y:"a",t:"迭代复盘之三员管理",O:-16941312e5},[":md"]],["v-81a71778","/daily/leverage-ai-to-boost-coding-productivity.html",{d:1693008e6,l:"2023年8月26日",g:["AI","Daily"],e:`

都什么年代了,还在用传统方式写代码?

-

前言

-

还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

-

本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

-

本文将分享 AI 时代的编程新实践,教你如何从一个 "Ctrl + C"、 "Ctrl + V" 工程师,变成一个 "Tab + Enter" 工程师🤣。

`,r:{minutes:7.37,words:2212},y:"a",t:"都什么年代了,还在用传统方式写代码?",O:-1693008e6},[":md"]],["v-5c48d497","/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html",{d:1694304e6,l:"2023年9月10日",g:["Daily","Video"],e:`

微软中国CTO演讲观后感

-

看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。

-`,r:{minutes:.17,words:51},y:"a",t:"微软中国CTO演讲观后感",O:-1694304e6},[":md"]],["v-1e305501","/daily/things-I-have-to-vent-about-vue.html",{d:16906752e5,l:"2023年7月30日",g:["Frontend","Daily","Video"],e:`

对Vue不得不吐槽的事

-

为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!

-

总结一下,我对Vue生态不满的地方在于:

-
    -
  1. Vue总是破坏性升级,新技术完全不管旧用户的体验
  2. -
  3. 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性
  4. -
-`,r:{minutes:.42,words:126},y:"a",t:"对Vue不得不吐槽的事",O:-16906752e5},[":md"]],["v-404740fa","/daily/use-claude2-instead-of-chatgpt.html",{d:16914528e5,l:"2023年8月8日",g:["Video","AI","Tool"],e:`

再见ChatGPT,我选择Claude2!

-

大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。

-

首先要评测的当然是ChatGPT了,因为最早用的就是它。

-`,r:{minutes:3.76,words:1128},y:"a",t:"再见ChatGPT,我选择Claude2!",O:-16914528e5},[":md"]],["v-f1efc11c","/daily/vim-creator-pass-away.html",{d:169128e7,l:"2023年8月6日",g:["Daily","Video"],e:`

Vim 作者离世

-

R.I.P 🙏

-

Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。

-

这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。

-`,r:{minutes:.24,words:71},y:"a",t:"Vim 作者离世",O:-169128e7},[":md"]],["v-414fd2b2","/daily/what-is-the-difference-between-sh-and-bash.html",{d:16910208e5,l:"2023年8月3日",g:["Linux","Docker","Video"],e:`

sh与bash的区别

-

结论:如果可移植性很重要,那么应该使用 sh!

-`,r:{minutes:.83,words:248},y:"a",t:"sh与bash的区别",O:-16910208e5},[":md"]],["v-bd2b5fe8","/daily/you-dont-need-to-add-tenant_id-to-every-table.html",{d:16949952e5,l:"2023年9月18日",g:["Design","Daily"],e:`

技术点评:别每张表都加tenant_id

-

前言

-

系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。

-`,r:{minutes:3.26,words:977},y:"a",t:"技术点评:别每张表都加tenant_id",O:-16949952e5},[":md"]],["v-971cc7fe","/english/contemporary-college-english-1.html",{d:16537824e5,l:"2022年5月29日",g:"English",e:`

现代大学英语精读(第2版)第一册

-

介绍

-

全书链接:https://www.ximalaya.com/album/43891910

-

虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。

-

虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。

`,r:{minutes:1.38,words:414},y:"a",t:"现代大学英语精读(第2版)第一册",O:-16537824e5},[":md"]],["v-93b316c0","/english/contemporary-college-english-2.html",{d:16543008e5,l:"2022年6月4日",g:"English",e:`

现代大学英语精读(第2版)第二册

-

介绍

-

全书链接:https://www.ximalaya.com/album/44290107

-

本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。

-

值得一读的文章

-

say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂

`,r:{minutes:1.96,words:588},y:"a",t:"现代大学英语精读(第2版)第二册",O:-16543008e5},[":md"]],["v-90496582","/english/contemporary-college-english-3.html",{d:16561152e5,l:"2022年6月25日",g:"English",e:`

现代大学英语精读(第2版)第三册

-

介绍

-

全书链接:https://www.ximalaya.com/album/44439108

-

阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。

`,r:{minutes:1.95,words:586},y:"a",t:"现代大学英语精读(第2版)第三册",O:-16561152e5},[":md"]],["v-8cdfb444","/english/contemporary-college-english-4.html",{d:16574976e5,l:"2022年7月11日",g:"English",e:`

现代大学英语精读(第2版)第四册

-

介绍

-

全书链接:https://www.ximalaya.com/album/44641280

-

本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。

-

值得一读的文章

-

Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从

`,r:{minutes:2.02,words:607},y:"a",t:"现代大学英语精读(第2版)第四册",O:-16574976e5},[":md"]],["v-89760306","/english/contemporary-college-english-5.html",{d:16616448e5,l:"2022年8月28日",g:"English",e:`

现代大学英语精读(第2版)第五册

-

介绍

-

全书链接:https://www.ximalaya.com/album/49466046

-

本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。

-

Who Are You and What Are You Doing Here

`,r:{minutes:7.18,words:2155},y:"a",t:"现代大学英语精读(第2版)第五册",O:-16616448e5},[":md"]],["v-860c51c8","/english/contemporary-college-english-6.html",{d:16622496e5,l:"2022年9月4日",g:"English",e:`

现代大学英语精读(第2版)第六册

-

前言

-

全书链接:https://www.ximalaya.com/album/49468954

-

本册是整个系列的最后一册了,完结撒花🎉

-

Paper Tigers

-

原文链接:https://www.ximalaya.com/sound/414998175

`,r:{minutes:5.08,words:1524},y:"a",t:"现代大学英语精读(第2版)第六册",O:-16622496e5},[":md"]],["v-246f4f17","/english/everyone-can-learn-english-1-overview.html",{d:16559424e5,l:"2022年6月23日",g:"English",e:`

人人都能学会的英语1:开篇

-

为什么学

-

不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。

-

也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语:

-
    -
  • 出国
  • -
  • 去外企
  • -
  • 有国际化需求
  • -
  • 学习专业领域的前沿知识
  • -
-

我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。

`,r:{minutes:4.46,words:1337},y:"a",t:"人人都能学会的英语1:开篇",O:-16559424e5},[":md"]],["v-3ac0474c","/english/everyone-can-learn-english-2-pronunciation.html",{d:16562016e5,l:"2022年6月26日",g:"English",e:`

人人都能学会的英语2:音标

-

方法

-

音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。

-

我推荐根据赖世雄的《美语音标》进行学习:

-
    -
  • 在微信公众号 常春藤英语集团 买相关书籍
  • -
  • 在 喜马拉雅 或 B站 听相关音频,进行跟读练习
  • -
  • 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格
  • -
-

有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。

`,r:{minutes:3.61,words:1083},y:"a",t:"人人都能学会的英语2:音标",O:-16562016e5},[":md"]],["v-58a409d7","/english/everyone-can-learn-english-3-words.html",{d:1656288e6,l:"2022年6月27日",g:"English",e:`

人人都能学会的英语3:单词

-

方法

-

单词是听说读写的基础,是有必要花时间去学习的。

-

然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议:

-
    -
  1. 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握
  2. -
  3. 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。
  4. -
  5. 只推荐利用词根记忆(当然并非百分百有效)
  6. -
  7. 学习软件:欧路词典——免费,全平台通用
  8. -
`,r:{minutes:4,words:1200},y:"a",t:"人人都能学会的英语3:单词",O:-1656288e6},[":md"]],["v-788d194a","/english/everyone-can-learn-english-4-listening-and-speaking.html",{d:16563744e5,l:"2022年6月28日",g:"English",e:`

人人都能学会的英语4:听说

-

前言

-

听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。

-

方法

-

听说属于综合能力,建议遵循以下学习步骤:

-
    -
  1. 激发兴趣,建立信心
  2. -
  3. 明确方向
  4. -
  5. 脚踏实地,坚持输入并输出
  6. -
-

1.建立信心

-

首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料:

-`,r:{minutes:3.82,words:1146},y:"a",t:"人人都能学会的英语4:听说",O:-16563744e5},[":md"]],["v-ae153a4e","/english/everyone-can-learn-english-5-reading-and-writing.html",{d:16564608e5,l:"2022年6月29日",g:"English",e:`

人人都能学会的英语5:读写

-

前言

-

读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。

-

阅读能力升级之旅

-

我先给出自己经历过的全英阅读能力的变化过程,仅供参考:

-
    -
  1. 看到英文网站,第一反应是点击切换中文版
  2. -
  3. 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读
  4. -
  5. 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域)
  6. -
  7. 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读
  8. -
  9. 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读
  10. -
  11. 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了
  12. -
`,r:{minutes:6.15,words:1846},y:"a",t:"人人都能学会的英语5:读写",O:-16564608e5},[":md"]],["v-d42db13c","/english/how-to-self-evaluate-english-level.html",{d:16573248e5,l:"2022年7月9日",g:"English",e:`

英文能力评测手把手教学

-

前言

-

之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。

-

单词量

-

进入网站:https://preply.com/en/learn/english/test-your-vocab

-

看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。
-image.png
-在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。
-image.png
-对于该结果,网站有以下值得注意的解释:

`,r:{minutes:1.85,words:554},y:"a",t:"英文能力评测手把手教学",O:-16573248e5},[":md"]],["v-6ed7d996","/english/learning-7000-words-task-completed.html",{d:16633728e5,l:"2022年9月17日",g:"English",e:`

完成刷7k单词任务

-

image.png
-这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。

-

首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。

`,r:{minutes:4.54,words:1362},y:"a",t:"完成刷7k单词任务",O:-16633728e5},[":md"]],["v-221efd1f","/english/let-chatgpt-be-your-foreign-language-teacher.html",{d:16833312e5,l:"2023年5月6日",g:["English","AI"],e:`

让 ChatGPT 成为你的外语私教

-

前言

-

有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。

-

本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。

-

准备工作

-

在开始之前,要准备好几样东西:

-
    -
  1. ChatGPT, 如果没有账号或不能上网,请查看上网教程
  2. -
  3. Chrome 浏览器插件 voice-control-for-chatgpt
  4. -
  5. 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明
  6. -
`,r:{minutes:2.83,words:849},y:"a",t:"让 ChatGPT 成为你的外语私教",O:-16833312e5},[":md"]],["v-72e84a92","/frontend/old-articles.html",{d:15674688e5,l:"2019年9月3日",g:"Frontend",e:`

旧文章精选

-`,r:{minutes:.3,words:90},y:"a",t:"旧文章精选",O:-15674688e5},[":md"]],["v-7320140c","/frontend/performance-optimization-in-action.html",{d:16117056e5,l:"2021年1月27日",g:"Frontend",e:`

前端项目性能优化实战

-

本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。

-`,r:{minutes:4.69,words:1407},y:"a",t:"前端项目性能优化实战",O:-16117056e5},[":md"]],["v-1e5872c4","/git/git-best-pratices.html",{d:16006464e5,l:"2020年9月21日",g:"Git",e:`

2020-09-21

-

Git最佳实践

-

精简提交

-

一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。
-如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。

-

频繁提交

-

一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。
-经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。

-

此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。

`,r:{minutes:3.86,words:1158},y:"a",t:"Git最佳实践",O:-16006464e5},[":md"]],["v-48e494ef","/git/git-definitive-guide-to-merge-code.html",{d:1651104e6,l:"2022年4月28日",g:["Git"],e:`

Git代码合并指南

-

前言

-

合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。
-在说明问题前,先定义一些概念:

-
    -
  • feat:指代功能分支
  • -
  • dev 与 test:指代两条不同的长驻分支,它们具有以下特点: -
      -
    • 受保护,不能直接推送
    • -
    • 不会被删除
    • -
    • 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test
    • -
    -
  • -
  • MR:merge request。代码合并请求
  • -
`,r:{minutes:3.45,words:1035},y:"a",t:"Git代码合并指南",O:-1651104e6},[":md"]],["v-60021cbc","/git/git-history-two-tricks-in-idea.html",{d:1691712e6,l:"2023年8月11日",g:["Git"],e:`

Git查看历史记录小技巧

-

分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。

-

这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。

-`,r:{minutes:2.16,words:648},y:"a",t:"Git查看历史记录小技巧",O:-1691712e6},[":md"]],["v-642eaaea","/git/git-useful-commands.html",{d:16006464e5,l:"2020年9月21日",g:"Git",e:`

Git常用命令

-

前言

-

本文将列举Git常见场景,并给出相应解决方案。

-

约定: 下文代码块中\${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。

-

推荐: 图形化交互式Git教程

-

配置

-

Mac/Linux 用户 执行以下操作

-
vi ~/.gitconfig
-
`,r:{minutes:5.05,words:1515},y:"a",t:"Git常用命令",O:-16006464e5},[":md"]],["v-4008ff77","/git/gitlab-ci.html",{d:16733088e5,l:"2023年1月10日",g:["Git","GitLab","Java","Node.js"],e:`

GitLab CI

-

前言

-

GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。

-`,r:{minutes:6.49,words:1946},y:"a",t:"GitLab CI",O:-16733088e5},[":md"]],["v-0fbf5fdc","/git/rethinking-git-flow.html",{d:16504992e5,l:"2022年4月21日",g:"Git",e:`

再论Git Flow

-

背景

-

团队目前使用的 Git 协作模式是:

-
    -
  1. 对每个功能建立相应的 feat 分支
  2. -
  3. 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat
  4. -
  5. 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支
  6. -
-

这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:

`,r:{minutes:5.53,words:1660},y:"a",t:"再论Git Flow",O:-16504992e5},[":md"]],["v-071be141","/git/use-command-line-tool-to-manage-gitlab-merge-request.html",{d:16795296e5,l:"2023年3月23日",g:["Git","GitLab","Python"],e:`

操作 Gitlab MR 的命令行工具

-

背景

-

为什么开发这个工具?主要解决以下问题:

-
    -
  1. 提测、上 UAT 时,避免漏合代码。
  2. -
  3. 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?”
  4. -
  5. 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。
  6. -
-

可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。
-并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。
-对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。

`,r:{minutes:4.42,words:1325},y:"a",t:"操作 Gitlab MR 的命令行工具",O:-16795296e5},[":md"]],["v-86a15cb6","/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html",{d:16693344e5,l:"2022年11月25日",g:["Java","Daily"],e:`

IDEA常见问题与解决方案

-

启动参数过长

-

Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun.
-解决方案:

-
    -
  1. 编辑 .idea/workspace.xml
  2. -
  3. 找到 PropertiesComponent
  4. -
  5. 添加:
  6. -
-

或者这样:
-image.png

`,r:{minutes:2.67,words:801},y:"a",t:"IDEA常见问题与解决方案",O:-16693344e5},[":md"]],["v-6dc18ec6","/java/Resolving-Common-Problems-in-Maven.md.html",{d:1670544e6,l:"2022年12月9日",g:["Java","Daily"],e:`

Maven常见问题与解决方案

-

运行 class 找不到主类

-
maven compile
-

得到 class 文件后

-
cd /my-app/target/com/mycompany/app
-java App
-
`,r:{minutes:3.28,words:984},y:"a",t:"Maven常见问题与解决方案",O:-1670544e6},[":md"]],["v-538a98d7","/java/avoid-sending-password-in-plaintext.html",{d:16981056e5,l:"2023年10月24日",g:["Java","JavaScript","Daily"],e:`

避免密码明文传输

-

说明

-

密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。

-

本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。

-

流程说明:前端加密,后端解密。

-

当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。

-`,r:{minutes:1.63,words:488},y:"a",t:"避免密码明文传输",O:-16981056e5},[":md"]],["v-2f6ae09c","/java/check-if-name-exists.html",{d:16975872e5,l:"2023年10月18日",g:["Java","MySQL","Daily"],e:`

检查名字是否重复

-

检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。

-`,r:{minutes:1.17,words:351},y:"a",t:"检查名字是否重复",O:-16975872e5},[":md"]],["v-b5a78a7a","/java/common-practices-for-handling-excel.html",{d:1693872e6,l:"2023年9月5日",g:["Java","Daily"],e:`

Excel处理常用实践

-

基础知识

-

导入需要用到对象,MultipartFile。

-
@PostMapping("/import")
-public boolean importLicense(
-      @RequestParam("file") MultipartFile file,
-      @RequestParam("tenantId") @NotBlank String tenantId,
-) {
-  return true;
-}
-
`,r:{minutes:4.82,words:1447},y:"a",t:"Excel处理常用实践",O:-1693872e6},[":md"]],["v-100d1814","/java/how-to-convert-snapshot-into-release-jar-without-source-code.html",{d:16917984e5,l:"2023年8月12日",g:["Java"],e:`

奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包

-

背景

-

项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。

-

问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。

-`,r:{minutes:1.41,words:422},y:"a",t:"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包",O:-16917984e5},[":md"]],["v-f5471cb0","/java/recommend-practices-for-query-by-date-range.html",{d:16944768e5,l:"2023年9月12日",g:["Java","Daily"],e:`

根据时间范围查询推荐实践

-

背景

-

不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。

-

虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。

-

本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson

-`,r:{minutes:3.21,words:962},y:"a",t:"根据时间范围查询推荐实践",O:-16944768e5},[":md"]],["v-1aafac08","/java/why-i-prefer-fastjson-instead-of-jackson.html",{d:1692576e6,l:"2023年8月21日",g:["Java","Daily","Video"],e:`

Jackson 经典异常 UnrecognizedPropertyException

-

原因是 json 包含的字段,多于 Java 实体类定义的字段。

-

解决方法很简单:

-
new ObjectMapper()
-  .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
-

或者为相关实体添加注解:

-
@JsonIgnoreProperties(ignoreUnknown = true)
-public class ObjectParseFromJsonString {  }
-
`,r:{minutes:.36,words:109},y:"a",t:"Jackson 经典异常 UnrecognizedPropertyException",O:-1692576e6},[":md"]],["v-ca672354","/java/why-is-it-so-hard-to-upgrade-dependencies.html",{d:1693008e6,l:"2023年8月26日",g:["Java","Daily","Video"],e:`

升个jar版本,怎么这么难?

-

前言

-

无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。

-

通常来说,对 Web 应用而言,前端是消费者,后端是提供者。

-

今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?

-`,r:{minutes:1.8,words:540},y:"a",t:"升个jar版本,怎么这么难?",O:-1693008e6},[":md"]],["v-143a8bce","/mysql/mysql-backup-case-study-mysqldump-in-action.html",{d:16922304e5,l:"2023年8月17日",g:["Daily","MySQL"],e:`

数据备份案例:mysqldump实战

-

背景

-

前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。

-

并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。

-

在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。

-`,r:{minutes:4.1,words:1231},y:"a",t:"数据备份案例:mysqldump实战",O:-16922304e5},[":md"]],["v-7e2c7a0c","/mysql/mysql-data-migration-case-study-add-auto-increment.html",{d:1692144e6,l:"2023年8月16日",g:["Daily","MySQL"],e:`

数据迁移案例:表AUTO_INCREMENT加10w

-

背景

-

项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。

-

问题分析:

-
    -
  1. 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。
  2. -
  3. 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。
  4. -
-

迁移思路:

-
    -
  1. 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条)
  2. -
  3. mysql-b 的相应的表 AUTO_INCREMENT 加 10w
  4. -
  5. 记得动手前先确保数据已备份
  6. -
-

注意:mysql-b的数据量比mysql-a的大,所以 mysql-b也直接设置AUTO_INCREMENT 加10w。
-但如果mysql-a的数量比较大,那是不可行的,此时mysql-b 需要先select 出每张表的最大id,作为需要增加的AUTO_INCREMENT。

-`,r:{minutes:2,words:599},y:"a",t:"数据迁移案例:表AUTO_INCREMENT加10w",O:-1692144e6},[":md"]],["v-f297935a","/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html",{d:16924032e5,l:"2023年8月19日",g:["Daily","MySQL"],e:`

MySQL 命令行执行SQL的细节

-

背景

-

经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了!

-

但要小心,在正式环境中执行 SQL,也许会有意想不到的坑!

-

环境说明

-

先说明下我们的环境信息。
-
-我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。

`,r:{minutes:3.02,words:906},y:"a",t:"MySQL 命令行执行SQL的细节",O:-16924032e5},[":md"]],["v-5b37b3c6","/python/export-mysql-table-into-excel.html",{d:16779744e5,l:"2023年3月5日",g:"Python",e:`

Python 导出 MySQL 库表信息到 Excel

-

需求

-

查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。

-`,r:{minutes:1.47,words:440},y:"a",t:"Python 导出 MySQL 库表信息到 Excel",O:-16779744e5},[":md"]],["v-113531b4","/software-testing/unit-testing-overview.html",{d:16905024e5,l:"2023年7月28日",g:["Testing"],e:`

单元测试概述

-

Why

-

为什么要做单元测试?或者说,为什么要写测试代码?

-

个人总结为以下两点:

-
    -
  1. 测试左移,降低修复bug的成本
    -
  2. -
  3. 形成资产,方便回归测试,后续迭代重构、维护有保障
  4. -
-`,r:{minutes:2.97,words:892},y:"a",t:"单元测试概述",O:-16905024e5},[":md"]],["v-65b23736","/software-testing/use-RestAssured-for-api-testing.html",{d:16862688e5,l:"2023年6月9日",g:["Java","Testing"],e:`

使用 RestAssured 进行 API 测试

-

前言

-

本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。

-

本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。

-

What

-

什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。

-

Why

-

为什么要做 API 测试呢?

`,r:{minutes:6.99,words:2096},y:"a",t:"使用 RestAssured 进行 API 测试",O:-16862688e5},[":md"]],["v-c488ac58","/software-testing/use-cypress-for-e2e-testing.html",{d:16073856e5,l:"2020年12月8日",g:["Node.js","Testing"],e:`

使用 Cypress 进行端对端测试

-

为什么写端对端测试

-

写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。

-

谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。

-

为什么用 Cypress

-

文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypress

`,r:{minutes:8.04,words:2413},y:"a",t:"使用 Cypress 进行端对端测试",O:-16073856e5},[":md"]],["v-efcacba2","/software-testing/use-jest-for-test-driven-development.html",{d:15558048e5,l:"2019年4月21日",g:["Node.js","Testing"],e:`

使用 Jest 实践测试驱动开发

-

前言

-

本文将使用jest进行测试驱动开发的示例,源码在github
-旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。

`,r:{minutes:6.26,words:1879},y:"a",t:"使用 Jest 实践测试驱动开发",O:-15558048e5},[":md"]],["v-0be1af08","/software-testing/use-playwright-for-ui-testing.html",{d:16834176e5,l:"2023年5月7日",g:["Node.js","Python","Testing"],e:`

下一代 UI 自动化测试工具 Playwright

-

前言

-

Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有:

-
    -
  1. 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具
  2. -
  3. 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件)
  4. -
-

另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。

`,r:{minutes:10,words:3001},y:"a",t:"下一代 UI 自动化测试工具 Playwright",O:-16834176e5},[":md"]],["v-75616b85","/software-testing/use-postman-for-api-testing.html",{d:16945632e5,l:"2023年9月13日",g:["Node.js","Daily"],e:`

使用 Postman 进行 API 测试

-

前言

-

虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。

-

而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。

-

还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:

-`,r:{minutes:5.41,words:1623},y:"a",t:"使用 Postman 进行 API 测试",O:-16945632e5},[":md"]],["v-17809471","/tools/how-to-connect-to-internet.html",{d:16556832e5,l:"2022年6月20日",g:"Tool",e:`

科学上网

-

说明

-

AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍?

-

本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。

-

购买指南

-

点击进入服务页面, 看到如下页面:
-image.png

`,r:{minutes:2.72,words:815},y:"a",t:"科学上网",O:-16556832e5},[":md"]],["v-3706649a","/404.html",{y:"p",t:"",O:null},[]],["v-79c9f96f","/daily/",{y:"p",t:"Daily"},[]],["v-43539db8","/english/",{y:"p",t:"English"},[]],["v-06198984","/frontend/",{y:"p",t:"Frontend"},[]],["v-74473916","/git/",{y:"p",t:"Git"},[]],["v-14c69af4","/java/",{y:"p",t:"Java"},[]],["v-eb072ff4","/mysql/",{y:"p",t:"Mysql"},[]],["v-63cd5dba","/python/",{y:"p",t:"Python"},[]],["v-0df55bac","/software-testing/",{y:"p",t:"Software Testing"},[]],["v-d440f426","/tools/",{y:"p",t:"Tools"},[]],["v-5bc93818","/category/",{y:"p",t:"分类",I:0},[]],["v-744d024e","/tag/",{y:"p",t:"标签",I:0},[]],["v-e52c881c","/article/",{y:"p",t:"文章",I:0},[]],["v-154dc4c4","/star/",{y:"p",t:"收藏",I:0},[]],["v-01560935","/timeline/",{y:"p",t:"时间轴",I:0},[]],["v-3d5315f8","/tag/daily/",{y:"p",t:"标签: Daily",I:0},[]],["v-1b3ae9cf","/tag/frontend/",{y:"p",t:"标签: Frontend",I:0},[]],["v-007c0ae2","/tag/video/",{y:"p",t:"标签: Video",I:0},[]],["v-211f44ee","/tag/linux/",{y:"p",t:"标签: Linux",I:0},[]],["v-6106c001","/tag/docker/",{y:"p",t:"标签: Docker",I:0},[]],["v-1bee38ca","/tag/mysql/",{y:"p",t:"标签: MySQL",I:0},[]],["v-0da0abf9","/tag/ai/",{y:"p",t:"标签: AI",I:0},[]],["v-5a3e80fc","/tag/emotion/",{y:"p",t:"标签: Emotion",I:0},[]],["v-01c1de5b","/tag/working-experience/",{y:"p",t:"标签: Working Experience",I:0},[]],["v-29350809","/tag/tool/",{y:"p",t:"标签: Tool",I:0},[]],["v-50d6e023","/tag/design/",{y:"p",t:"标签: Design",I:0},[]],["v-0ca0efe6","/tag/english/",{y:"p",t:"标签: English",I:0},[]],["v-b310d42a","/tag/git/",{y:"p",t:"标签: Git",I:0},[]],["v-13275df4","/tag/gitlab/",{y:"p",t:"标签: GitLab",I:0},[]],["v-28a1d8bf","/tag/java/",{y:"p",t:"标签: Java",I:0},[]],["v-60379330","/tag/node.js/",{y:"p",t:"标签: Node.js",I:0},[]],["v-245f5676","/tag/python/",{y:"p",t:"标签: Python",I:0},[]],["v-3b951558","/tag/javascript/",{y:"p",t:"标签: JavaScript",I:0},[]],["v-48b3e46d","/tag/testing/",{y:"p",t:"标签: Testing",I:0},[]]];var Ts=M({name:"Vuepress",setup(){const e=$0();return()=>s(e.value)}}),W2=()=>G2.reduce((e,[t,l,n,a])=>(e.push({name:t,path:l,component:Ts,meta:n},{path:l.endsWith("/")?l+"index.html":l.substring(0,l.length-5),redirect:l},...a.map(i=>({path:i===":md"?l.substring(0,l.length-5)+".md":i,redirect:l}))),e),[{name:"404",path:"/:catchAll(.*)",component:Ts}]),K2=n1,J2=()=>{const e=j1({history:K2(Li("/")),routes:W2(),scrollBehavior:(t,l,n)=>n||(t.hash?{el:t.hash}:{top:0})});return e.beforeResolve(async(t,l)=>{var n;(t.path!==l.path||l===Et)&&([Dt.value]=await Promise.all([wt.resolvePageData(t.name),(n=Oo[t.name])==null?void 0:n.__asyncLoader()]))}),e},Q2=e=>{e.component("ClientOnly",Zn),e.component("Content",No)},Y2=(e,t,l)=>{const n=E(()=>wt.resolveLayouts(l)),a=es(()=>t.currentRoute.value.path),i=es(()=>wt.resolveRouteLocale(ol.value.locales,a.value)),r=E(()=>wt.resolveSiteLocaleData(ol.value,i.value)),o=E(()=>wt.resolvePageFrontmatter(Dt.value)),c=E(()=>wt.resolvePageHeadTitle(Dt.value,r.value)),u=E(()=>wt.resolvePageHead(c.value,o.value,r.value)),d=E(()=>wt.resolvePageLang(Dt.value,r.value)),p=E(()=>wt.resolvePageLayout(Dt.value,n.value));return e.provide(C0,n),e.provide(Ro,o),e.provide(D0,c),e.provide(So,u),e.provide(Do,d),e.provide(Mo,p),e.provide(Ti,i),e.provide(Fo,r),Object.defineProperties(e.config.globalProperties,{$frontmatter:{get:()=>o.value},$head:{get:()=>u.value},$headTitle:{get:()=>c.value},$lang:{get:()=>d.value},$page:{get:()=>Dt.value},$routeLocale:{get:()=>i.value},$site:{get:()=>ol.value},$siteLocale:{get:()=>r.value},$withBase:{get:()=>Le}}),{layouts:n,pageData:Dt,pageFrontmatter:o,pageHead:u,pageHeadTitle:c,pageLang:d,pageLayout:p,routeLocale:i,siteData:ol,siteLocaleData:r}},X2=()=>{const e=S0(),t=$o(),l=W([]),n=()=>{e.value.forEach(i=>{const r=Z2(i);r&&l.value.push(r)})},a=()=>{document.documentElement.lang=t.value,l.value.forEach(i=>{i.parentNode===document.head&&document.head.removeChild(i)}),l.value.splice(0,l.value.length),e.value.forEach(i=>{const r=eg(i);r!==null&&(document.head.appendChild(r),l.value.push(r))})};ot(M0,a),ke(()=>{n(),a(),ce(()=>e.value,a)})},Z2=([e,t,l=""])=>{const n=Object.entries(t).map(([o,c])=>ae(c)?`[${o}=${JSON.stringify(c)}]`:c===!0?`[${o}]`:"").join(""),a=`head > ${e}${n}`;return Array.from(document.querySelectorAll(a)).find(o=>o.innerText===l)||null},eg=([e,t,l])=>{if(!ae(e))return null;const n=document.createElement(e);return xi(t)&&Object.entries(t).forEach(([a,i])=>{ae(i)?n.setAttribute(a,i):i===!0&&n.setAttribute(a,"")}),ae(l)&&n.appendChild(document.createTextNode(l)),n},tg=_0,lg=async()=>{var l;const e=tg({name:"VuepressApp",setup(){var n;X2();for(const a of Cn)(n=a.setup)==null||n.call(a);return()=>[s(Qo),...Cn.flatMap(({rootComponents:a=[]})=>a.map(i=>s(i)))]}}),t=J2();Q2(e),Y2(e,t,Cn);for(const n of Cn)await((l=n.enhance)==null?void 0:l.call(n,{app:e,router:t,siteData:ol}));return e.use(t),{app:e,router:t}};lg().then(({app:e,router:t})=>{t.isReady().then(()=>{e.mount("#app")})});export{mo as a,yo as b,ag as c,lg as createVueApp,Ae as d,rg as e,ig as f,Ip as o,Je as r,qd as w}; diff --git a/assets/avoid-sending-password-in-plaintext.html-6233e8a7.js b/assets/avoid-sending-password-in-plaintext.html-7173da3b.js similarity index 91% rename from assets/avoid-sending-password-in-plaintext.html-6233e8a7.js rename to assets/avoid-sending-password-in-plaintext.html-7173da3b.js index 35551d7d..6e8548be 100644 --- a/assets/avoid-sending-password-in-plaintext.html-6233e8a7.js +++ b/assets/avoid-sending-password-in-plaintext.html-7173da3b.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-538a98d7","path":"/java/avoid-sending-password-in-plaintext.html","title":"避免密码明文传输","lang":"zh-CN","frontmatter":{"date":"2023-10-24T00:00:00.000Z","tag":["Java","JavaScript","Daily"],"description":"避免密码明文传输 说明 密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。 本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。 流程说明:前端加密,后端解密。 当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/avoid-sending-password-in-plaintext.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"避免密码明文传输"}],["meta",{"property":"og:description","content":"避免密码明文传输 说明 密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。 本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。 流程说明:前端加密,后端解密。 当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"JavaScript"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-10-24T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"避免密码明文传输\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-10-24T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"说明","slug":"说明","link":"#说明","children":[]},{"level":2,"title":"前端代码","slug":"前端代码","link":"#前端代码","children":[]},{"level":2,"title":"后端代码","slug":"后端代码","link":"#后端代码","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.63,"words":488},"filePathRelative":"java/avoid-sending-password-in-plaintext.md","localizedDate":"2023年10月24日","excerpt":"

避免密码明文传输

\\n

说明

\\n

密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。

\\n

本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。

\\n

流程说明:前端加密,后端解密。

\\n

当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-538a98d7","path":"/java/avoid-sending-password-in-plaintext.html","title":"避免密码明文传输","lang":"zh-CN","frontmatter":{"date":"2023-10-24T00:00:00.000Z","tag":["Java","JavaScript","Daily"],"description":"避免密码明文传输 说明 密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。 本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。 流程说明:前端加密,后端解密。 当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/avoid-sending-password-in-plaintext.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"避免密码明文传输"}],["meta",{"property":"og:description","content":"避免密码明文传输 说明 密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。 本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。 流程说明:前端加密,后端解密。 当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"JavaScript"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-10-24T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"避免密码明文传输\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-10-24T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"说明","slug":"说明","link":"#说明","children":[]},{"level":2,"title":"前端代码","slug":"前端代码","link":"#前端代码","children":[]},{"level":2,"title":"后端代码","slug":"后端代码","link":"#后端代码","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.63,"words":488},"filePathRelative":"java/avoid-sending-password-in-plaintext.md","localizedDate":"2023年10月24日","excerpt":"

避免密码明文传输

\\n

说明

\\n

密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。

\\n

本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。

\\n

流程说明:前端加密,后端解密。

\\n

当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/avoid-sending-password-in-plaintext.html-43f53b8a.js b/assets/avoid-sending-password-in-plaintext.html-e9e5371d.js similarity index 99% rename from assets/avoid-sending-password-in-plaintext.html-43f53b8a.js rename to assets/avoid-sending-password-in-plaintext.html-e9e5371d.js index 01081136..8b563d4e 100644 --- a/assets/avoid-sending-password-in-plaintext.html-43f53b8a.js +++ b/assets/avoid-sending-password-in-plaintext.html-e9e5371d.js @@ -1,4 +1,4 @@ -import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c as p,e,a as n,b as s,f as c}from"./app-b649ee34.js";const o={},l=n("h1",{id:"避免密码明文传输",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#避免密码明文传输","aria-hidden":"true"},"#"),s(" 避免密码明文传输")],-1),i=n("h2",{id:"说明",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#说明","aria-hidden":"true"},"#"),s(" 说明")],-1),u=n("p",null,"密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。",-1),k=n("p",null,"本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。",-1),r=n("p",null,"流程说明:前端加密,后端解密。",-1),d=n("p",null,"当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。",-1),v=c(`

前端代码

记得安装相应的 npm 模块。

/*******************************
+import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c as p,e,a as n,b as s,f as c}from"./app-a9d55428.js";const o={},l=n("h1",{id:"避免密码明文传输",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#避免密码明文传输","aria-hidden":"true"},"#"),s(" 避免密码明文传输")],-1),i=n("h2",{id:"说明",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#说明","aria-hidden":"true"},"#"),s(" 说明")],-1),u=n("p",null,"密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。",-1),k=n("p",null,"本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。",-1),r=n("p",null,"流程说明:前端加密,后端解密。",-1),d=n("p",null,"当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。",-1),v=c(`

前端代码

记得安装相应的 npm 模块。

/*******************************
 Description: aes加解密工具方法
 ********************************/
 import AES from 'crypto-js/aes'
diff --git a/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-5673c414.js b/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-77f7e2a5.js
similarity index 93%
rename from assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-5673c414.js
rename to assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-77f7e2a5.js
index a0e26ea1..66da645c 100644
--- a/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-5673c414.js
+++ b/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-77f7e2a5.js
@@ -1 +1 @@
-const t=JSON.parse('{"key":"v-79e139f8","path":"/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html","title":"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?","lang":"zh-CN","frontmatter":{"date":"2023-08-15T00:00:00.000Z","tag":["Daily","Video","MySQL"],"description":"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci? Background Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci? CREATE TABLE `my_table` ( `id` bigint NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; That is the knowledge that today I want to share with you.","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?"}],["meta",{"property":"og:description","content":"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci? Background Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci? CREATE TABLE `my_table` ( `id` bigint NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; That is the knowledge that today I want to share with you."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:published_time","content":"2023-08-15T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-15T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"Background","slug":"background","link":"#background","children":[]},{"level":2,"title":"utf8mb4(UTF-8 MultiByte 4-Byte)","slug":"utf8mb4-utf-8-multibyte-4-byte","link":"#utf8mb4-utf-8-multibyte-4-byte","children":[]},{"level":2,"title":"utf8mb4_unicode_ci","slug":"utf8mb4-unicode-ci","link":"#utf8mb4-unicode-ci","children":[]},{"level":2,"title":"Some tips","slug":"some-tips","link":"#some-tips","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.78,"words":533},"filePathRelative":"daily/beyond-utf8-do-you-know-utf8mb4-and-collation.md","localizedDate":"2023年8月15日","excerpt":"

Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

\\n

Background

\\n

Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci?

\\n
CREATE TABLE `my_table` (\\n  `id` bigint NOT NULL AUTO_INCREMENT,\\n  PRIMARY KEY (`id`) USING BTREE\\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\\n

That is the knowledge that today I want to share with you.

\\n","autoDesc":true}');export{t as data}; +const t=JSON.parse('{"key":"v-79e139f8","path":"/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html","title":"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?","lang":"zh-CN","frontmatter":{"date":"2023-08-15T00:00:00.000Z","tag":["Daily","Video","MySQL"],"description":"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci? Background Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci? CREATE TABLE `my_table` ( `id` bigint NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; That is the knowledge that today I want to share with you.","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?"}],["meta",{"property":"og:description","content":"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci? Background Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci? CREATE TABLE `my_table` ( `id` bigint NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; That is the knowledge that today I want to share with you."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:published_time","content":"2023-08-15T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-15T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"Background","slug":"background","link":"#background","children":[]},{"level":2,"title":"utf8mb4(UTF-8 MultiByte 4-Byte)","slug":"utf8mb4-utf-8-multibyte-4-byte","link":"#utf8mb4-utf-8-multibyte-4-byte","children":[]},{"level":2,"title":"utf8mb4_unicode_ci","slug":"utf8mb4-unicode-ci","link":"#utf8mb4-unicode-ci","children":[]},{"level":2,"title":"Some tips","slug":"some-tips","link":"#some-tips","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.78,"words":533},"filePathRelative":"daily/beyond-utf8-do-you-know-utf8mb4-and-collation.md","localizedDate":"2023年8月15日","excerpt":"

Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

\\n

Background

\\n

Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci?

\\n
CREATE TABLE `my_table` (\\n  `id` bigint NOT NULL AUTO_INCREMENT,\\n  PRIMARY KEY (`id`) USING BTREE\\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\\n

That is the knowledge that today I want to share with you.

\\n","autoDesc":true}');export{t as data}; diff --git a/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-85d76568.js b/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-d98b859a.js similarity index 99% rename from assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-85d76568.js rename to assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-d98b859a.js index 88ad5b8d..39c52e22 100644 --- a/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-85d76568.js +++ b/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-d98b859a.js @@ -1,4 +1,4 @@ -import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{r as s,o as t,c as o,e as i,d as c,f as e}from"./app-b649ee34.js";const r={},l=e('

Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

Background

Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci?

CREATE TABLE `my_table` (\n  `id` bigint NOT NULL AUTO_INCREMENT,\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n

That is the knowledge that today I want to share with you.

',5),d=e(`

utf8mb4(UTF-8 MultiByte 4-Byte)

UTF-8 was initially designed to support characters from the Unicode standard, which includes characters from various writing systems used across different languages.

And the original UTF-8 encoding was Basic Multilingual Plane (BMP), which is a specific range of Unicode code points from U+0000 to U+FFFF, including a total of 65,536 code points, using 1 to 3 bytes.

However, as the Unicode standard expanded to include more characters beyond the BMP, there arose a need for a new encoding to accommodate these additional characters. This is where utf8mb4 comes into play.

The key differences between utf8 and utf8mb4 are:

  1. Character Range: utf8mb4 supports the entire Unicode character range, while utf8 is limited to the BMP.
  2. Number of Bytes: utf8 characters can be stored using 1 to 3 bytes, while utf8mb4 characters can use up to 4 bytes.

In practical terms, if you want to store or display characters beyond the BMP (e.g., emojis) in your MySQL database, you need to use the utf8mb4 character set.

utf8mb4_unicode_ci

This is about Collation.

Collation determines the mechanism of string comparisons, specifically regarding sorting and searching.

Let's take **utf8mb4_unicode_ci and utf8mb4_general_ci **for examples. Since ci stands for case-insensitive, both of them ignore differences in lettercase.

And their main differences are:

  1. utf8mb4_unicode_ci:
    • This collation provides a more comprehensive and accurate comparison algorithm based on the Unicode standard.
    • It is generally more suitable when dealing with multilingual applications or when precise sorting and comparisons are required.
  2. utf8mb4_general_ci:
    • This collation is generally faster for sorting and comparisons.
    • Its comparison algorithm would also ignore differences in certain character variations (such as accents or diacritics).
    • However, it may not produce accurate results when dealing with some complex language-specific sorting and comparison rules, because it might treat accented characters as identical to their unaccented counterparts.

And here're some examples of Accented Characters in Latin-based Languages:

  • á (acute accent) - Unaccented: a
  • ä (umlaut/diaeresis) - Unaccented: a

As a general recommendation, utf8mb4_unicode_ci is often considered a better default choice, especially in applications with internationalization (i18n) requirements. It provides more accurate sorting and comparison results for a wide range of languages and characters.
However, there may be some specific use cases where utf8mb4_general_ci is preferred, such as when performance is a critical concern and language-specific sorting rules are not essential.

Some tips

You can use the following command to check default collation for your MySQL database:

SHOW VARIABLES LIKE 'collation_database';
+import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{r as s,o as t,c as o,e as i,d as c,f as e}from"./app-a9d55428.js";const r={},l=e('

Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

Background

Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci?

CREATE TABLE `my_table` (\n  `id` bigint NOT NULL AUTO_INCREMENT,\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n

That is the knowledge that today I want to share with you.

',5),d=e(`

utf8mb4(UTF-8 MultiByte 4-Byte)

UTF-8 was initially designed to support characters from the Unicode standard, which includes characters from various writing systems used across different languages.

And the original UTF-8 encoding was Basic Multilingual Plane (BMP), which is a specific range of Unicode code points from U+0000 to U+FFFF, including a total of 65,536 code points, using 1 to 3 bytes.

However, as the Unicode standard expanded to include more characters beyond the BMP, there arose a need for a new encoding to accommodate these additional characters. This is where utf8mb4 comes into play.

The key differences between utf8 and utf8mb4 are:

  1. Character Range: utf8mb4 supports the entire Unicode character range, while utf8 is limited to the BMP.
  2. Number of Bytes: utf8 characters can be stored using 1 to 3 bytes, while utf8mb4 characters can use up to 4 bytes.

In practical terms, if you want to store or display characters beyond the BMP (e.g., emojis) in your MySQL database, you need to use the utf8mb4 character set.

utf8mb4_unicode_ci

This is about Collation.

Collation determines the mechanism of string comparisons, specifically regarding sorting and searching.

Let's take **utf8mb4_unicode_ci and utf8mb4_general_ci **for examples. Since ci stands for case-insensitive, both of them ignore differences in lettercase.

And their main differences are:

  1. utf8mb4_unicode_ci:
    • This collation provides a more comprehensive and accurate comparison algorithm based on the Unicode standard.
    • It is generally more suitable when dealing with multilingual applications or when precise sorting and comparisons are required.
  2. utf8mb4_general_ci:
    • This collation is generally faster for sorting and comparisons.
    • Its comparison algorithm would also ignore differences in certain character variations (such as accents or diacritics).
    • However, it may not produce accurate results when dealing with some complex language-specific sorting and comparison rules, because it might treat accented characters as identical to their unaccented counterparts.

And here're some examples of Accented Characters in Latin-based Languages:

  • á (acute accent) - Unaccented: a
  • ä (umlaut/diaeresis) - Unaccented: a

As a general recommendation, utf8mb4_unicode_ci is often considered a better default choice, especially in applications with internationalization (i18n) requirements. It provides more accurate sorting and comparison results for a wide range of languages and characters.
However, there may be some specific use cases where utf8mb4_general_ci is preferred, such as when performance is a critical concern and language-specific sorting rules are not essential.

Some tips

You can use the following command to check default collation for your MySQL database:

SHOW VARIABLES LIKE 'collation_database';
 

You may encounter this error Illegal mix of collations:

select id from my_table where tenant_id=@target_tenant_id;
 
 Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) 
diff --git a/assets/check-if-name-exists.html-4b9b7d86.js b/assets/check-if-name-exists.html-20410c4b.js
similarity index 88%
rename from assets/check-if-name-exists.html-4b9b7d86.js
rename to assets/check-if-name-exists.html-20410c4b.js
index 7aa991a1..0568503b 100644
--- a/assets/check-if-name-exists.html-4b9b7d86.js
+++ b/assets/check-if-name-exists.html-20410c4b.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-2f6ae09c","path":"/java/check-if-name-exists.html","title":"检查名字是否重复","lang":"zh-CN","frontmatter":{"date":"2023-10-18T00:00:00.000Z","tag":["Java","MySQL","Daily"],"description":"检查名字是否重复 检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/check-if-name-exists.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"检查名字是否重复"}],["meta",{"property":"og:description","content":"检查名字是否重复 检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-10-18T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"检查名字是否重复\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-10-18T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"推荐做法","slug":"推荐做法","link":"#推荐做法","children":[]},{"level":2,"title":"Why","slug":"why","link":"#why","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.17,"words":351},"filePathRelative":"java/check-if-name-exists.md","localizedDate":"2023年10月18日","excerpt":"

检查名字是否重复

\\n

检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-2f6ae09c","path":"/java/check-if-name-exists.html","title":"检查名字是否重复","lang":"zh-CN","frontmatter":{"date":"2023-10-18T00:00:00.000Z","tag":["Java","MySQL","Daily"],"description":"检查名字是否重复 检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/check-if-name-exists.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"检查名字是否重复"}],["meta",{"property":"og:description","content":"检查名字是否重复 检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-10-18T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"检查名字是否重复\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-10-18T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"推荐做法","slug":"推荐做法","link":"#推荐做法","children":[]},{"level":2,"title":"Why","slug":"why","link":"#why","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.17,"words":351},"filePathRelative":"java/check-if-name-exists.md","localizedDate":"2023年10月18日","excerpt":"

检查名字是否重复

\\n

检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/check-if-name-exists.html-abc8b8dd.js b/assets/check-if-name-exists.html-488fca17.js similarity index 99% rename from assets/check-if-name-exists.html-abc8b8dd.js rename to assets/check-if-name-exists.html-488fca17.js index 3939d3c4..904c01ad 100644 --- a/assets/check-if-name-exists.html-abc8b8dd.js +++ b/assets/check-if-name-exists.html-488fca17.js @@ -1,4 +1,4 @@ -import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{o as a,c as t,e as p,a as n,b as e,f as o}from"./app-b649ee34.js";const c={},l=n("h1",{id:"检查名字是否重复",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#检查名字是否重复","aria-hidden":"true"},"#"),e(" 检查名字是否重复")],-1),i=n("p",null,"检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。",-1),u=o(`

推荐做法

借助数据库的来实现,执行以下语句:

ALTER TABLE my_table ADD UNIQUE(name);
+import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{o as a,c as t,e as p,a as n,b as e,f as o}from"./app-a9d55428.js";const c={},l=n("h1",{id:"检查名字是否重复",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#检查名字是否重复","aria-hidden":"true"},"#"),e(" 检查名字是否重复")],-1),i=n("p",null,"检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。",-1),u=o(`

推荐做法

借助数据库的来实现,执行以下语句:

ALTER TABLE my_table ADD UNIQUE(name);
 

然后,在程序里添加全局异常处理类:

@Slf4j
 @RestControllerAdvice
 public class GlobalExceptionHandler {
diff --git a/assets/claude-ai-in-action-extract-info-from-html.html-2568b3bd.js b/assets/claude-ai-in-action-extract-info-from-html.html-47bc02a1.js
similarity index 82%
rename from assets/claude-ai-in-action-extract-info-from-html.html-2568b3bd.js
rename to assets/claude-ai-in-action-extract-info-from-html.html-47bc02a1.js
index 2074811b..0ed75949 100644
--- a/assets/claude-ai-in-action-extract-info-from-html.html-2568b3bd.js
+++ b/assets/claude-ai-in-action-extract-info-from-html.html-47bc02a1.js
@@ -1 +1 @@
-import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as c,c as i,e as n,d as r,a as e,b as l}from"./app-b649ee34.js";const s={},d=e("h1",{id:"claude-ai应用案例-从html中抽取文本",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#claude-ai应用案例-从html中抽取文本","aria-hidden":"true"},"#"),l(" Claude AI应用案例,从HTML中抽取文本")],-1),m=e("p",null,"今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!",-1);function _(h,u){const t=o("BiliBili");return c(),i("div",null,[d,m,n(" more "),r(t,{bvid:"BV13u4y1D7Kj"})])}const B=a(s,[["render",_],["__file","claude-ai-in-action-extract-info-from-html.html.vue"]]);export{B as default};
+import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as c,c as i,e as n,d as r,a as e,b as l}from"./app-a9d55428.js";const s={},d=e("h1",{id:"claude-ai应用案例-从html中抽取文本",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#claude-ai应用案例-从html中抽取文本","aria-hidden":"true"},"#"),l(" Claude AI应用案例,从HTML中抽取文本")],-1),m=e("p",null,"今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!",-1);function _(h,u){const t=o("BiliBili");return c(),i("div",null,[d,m,n(" more "),r(t,{bvid:"BV13u4y1D7Kj"})])}const B=a(s,[["render",_],["__file","claude-ai-in-action-extract-info-from-html.html.vue"]]);export{B as default};
diff --git a/assets/claude-ai-in-action-extract-info-from-html.html-2c1451df.js b/assets/claude-ai-in-action-extract-info-from-html.html-cd27a8d3.js
similarity index 89%
rename from assets/claude-ai-in-action-extract-info-from-html.html-2c1451df.js
rename to assets/claude-ai-in-action-extract-info-from-html.html-cd27a8d3.js
index c5990144..1bcfe863 100644
--- a/assets/claude-ai-in-action-extract-info-from-html.html-2c1451df.js
+++ b/assets/claude-ai-in-action-extract-info-from-html.html-cd27a8d3.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-7a74360a","path":"/daily/claude-ai-in-action-extract-info-from-html.html","title":"Claude AI应用案例,从HTML中抽取文本","lang":"zh-CN","frontmatter":{"date":"2023-08-25T00:00:00.000Z","tag":["Daily","Video","AI"],"description":"Claude AI应用案例,从HTML中抽取文本 今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/claude-ai-in-action-extract-info-from-html.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Claude AI应用案例,从HTML中抽取文本"}],["meta",{"property":"og:description","content":"Claude AI应用案例,从HTML中抽取文本 今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:tag","content":"AI"}],["meta",{"property":"article:published_time","content":"2023-08-25T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Claude AI应用案例,从HTML中抽取文本\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-25T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.19,"words":58},"filePathRelative":"daily/claude-ai-in-action-extract-info-from-html.md","localizedDate":"2023年8月25日","excerpt":"

Claude AI应用案例,从HTML中抽取文本

\\n

今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-7a74360a","path":"/daily/claude-ai-in-action-extract-info-from-html.html","title":"Claude AI应用案例,从HTML中抽取文本","lang":"zh-CN","frontmatter":{"date":"2023-08-25T00:00:00.000Z","tag":["Daily","Video","AI"],"description":"Claude AI应用案例,从HTML中抽取文本 今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/claude-ai-in-action-extract-info-from-html.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Claude AI应用案例,从HTML中抽取文本"}],["meta",{"property":"og:description","content":"Claude AI应用案例,从HTML中抽取文本 今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:tag","content":"AI"}],["meta",{"property":"article:published_time","content":"2023-08-25T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Claude AI应用案例,从HTML中抽取文本\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-25T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.19,"words":58},"filePathRelative":"daily/claude-ai-in-action-extract-info-from-html.md","localizedDate":"2023年8月25日","excerpt":"

Claude AI应用案例,从HTML中抽取文本

\\n

今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/common-practices-for-handling-excel.html-0efdabf9.js b/assets/common-practices-for-handling-excel.html-c351dd73.js similarity index 93% rename from assets/common-practices-for-handling-excel.html-0efdabf9.js rename to assets/common-practices-for-handling-excel.html-c351dd73.js index fb8687fb..0a2a3394 100644 --- a/assets/common-practices-for-handling-excel.html-0efdabf9.js +++ b/assets/common-practices-for-handling-excel.html-c351dd73.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-b5a78a7a","path":"/java/common-practices-for-handling-excel.html","title":"Excel处理常用实践","lang":"zh-CN","frontmatter":{"date":"2023-09-05T00:00:00.000Z","tag":["Java","Daily"],"description":"Excel处理常用实践 基础知识 导入需要用到对象,MultipartFile。 @PostMapping(\\"/import\\") public boolean importLicense( @RequestParam(\\"file\\") MultipartFile file, @RequestParam(\\"tenantId\\") @NotBlank String tenantId, ) { return true; }","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/common-practices-for-handling-excel.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Excel处理常用实践"}],["meta",{"property":"og:description","content":"Excel处理常用实践 基础知识 导入需要用到对象,MultipartFile。 @PostMapping(\\"/import\\") public boolean importLicense( @RequestParam(\\"file\\") MultipartFile file, @RequestParam(\\"tenantId\\") @NotBlank String tenantId, ) { return true; }"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-05T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Excel处理常用实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-05T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"基础知识","slug":"基础知识","link":"#基础知识","children":[]},{"level":2,"title":"应用框架","slug":"应用框架","link":"#应用框架","children":[{"level":3,"title":"导出","slug":"导出","link":"#导出","children":[]},{"level":3,"title":"导入","slug":"导入","link":"#导入","children":[]}]},{"level":2,"title":"常见问题与解决方案","slug":"常见问题与解决方案","link":"#常见问题与解决方案","children":[{"level":3,"title":"浏览器下载","slug":"浏览器下载","link":"#浏览器下载","children":[]},{"level":3,"title":"上传文件大小限制","slug":"上传文件大小限制","link":"#上传文件大小限制","children":[]},{"level":3,"title":"缺少字体","slug":"缺少字体","link":"#缺少字体","children":[]},{"level":3,"title":"序列化失败","slug":"序列化失败","link":"#序列化失败","children":[]}]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.82,"words":1447},"filePathRelative":"java/common-practices-for-handling-excel.md","localizedDate":"2023年9月5日","excerpt":"

Excel处理常用实践

\\n

基础知识

\\n

导入需要用到对象,MultipartFile。

\\n
@PostMapping(\\"/import\\")\\npublic boolean importLicense(\\n      @RequestParam(\\"file\\") MultipartFile file,\\n      @RequestParam(\\"tenantId\\") @NotBlank String tenantId,\\n) {\\n  return true;\\n}\\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-b5a78a7a","path":"/java/common-practices-for-handling-excel.html","title":"Excel处理常用实践","lang":"zh-CN","frontmatter":{"date":"2023-09-05T00:00:00.000Z","tag":["Java","Daily"],"description":"Excel处理常用实践 基础知识 导入需要用到对象,MultipartFile。 @PostMapping(\\"/import\\") public boolean importLicense( @RequestParam(\\"file\\") MultipartFile file, @RequestParam(\\"tenantId\\") @NotBlank String tenantId, ) { return true; }","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/common-practices-for-handling-excel.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Excel处理常用实践"}],["meta",{"property":"og:description","content":"Excel处理常用实践 基础知识 导入需要用到对象,MultipartFile。 @PostMapping(\\"/import\\") public boolean importLicense( @RequestParam(\\"file\\") MultipartFile file, @RequestParam(\\"tenantId\\") @NotBlank String tenantId, ) { return true; }"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-05T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Excel处理常用实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-05T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"基础知识","slug":"基础知识","link":"#基础知识","children":[]},{"level":2,"title":"应用框架","slug":"应用框架","link":"#应用框架","children":[{"level":3,"title":"导出","slug":"导出","link":"#导出","children":[]},{"level":3,"title":"导入","slug":"导入","link":"#导入","children":[]}]},{"level":2,"title":"常见问题与解决方案","slug":"常见问题与解决方案","link":"#常见问题与解决方案","children":[{"level":3,"title":"浏览器下载","slug":"浏览器下载","link":"#浏览器下载","children":[]},{"level":3,"title":"上传文件大小限制","slug":"上传文件大小限制","link":"#上传文件大小限制","children":[]},{"level":3,"title":"缺少字体","slug":"缺少字体","link":"#缺少字体","children":[]},{"level":3,"title":"序列化失败","slug":"序列化失败","link":"#序列化失败","children":[]}]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.82,"words":1447},"filePathRelative":"java/common-practices-for-handling-excel.md","localizedDate":"2023年9月5日","excerpt":"

Excel处理常用实践

\\n

基础知识

\\n

导入需要用到对象,MultipartFile。

\\n
@PostMapping(\\"/import\\")\\npublic boolean importLicense(\\n      @RequestParam(\\"file\\") MultipartFile file,\\n      @RequestParam(\\"tenantId\\") @NotBlank String tenantId,\\n) {\\n  return true;\\n}\\n
","autoDesc":true}');export{e as data}; diff --git a/assets/common-practices-for-handling-excel.html-82289612.js b/assets/common-practices-for-handling-excel.html-d03a1dea.js similarity index 99% rename from assets/common-practices-for-handling-excel.html-82289612.js rename to assets/common-practices-for-handling-excel.html-d03a1dea.js index 62c5d6d8..10827da8 100644 --- a/assets/common-practices-for-handling-excel.html-82289612.js +++ b/assets/common-practices-for-handling-excel.html-d03a1dea.js @@ -1,4 +1,4 @@ -import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{o as s,c as a,f as t}from"./app-b649ee34.js";const p={},e=t(`

Excel处理常用实践

基础知识

导入需要用到对象,MultipartFile。

@PostMapping("/import")
+import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{o as s,c as a,f as t}from"./app-a9d55428.js";const p={},e=t(`

Excel处理常用实践

基础知识

导入需要用到对象,MultipartFile。

@PostMapping("/import")
 public boolean importLicense(
       @RequestParam("file") MultipartFile file,
       @RequestParam("tenantId") @NotBlank String tenantId,
diff --git a/assets/contemporary-college-english-1.html-6b25fa3f.js b/assets/contemporary-college-english-1.html-49632c93.js
similarity index 91%
rename from assets/contemporary-college-english-1.html-6b25fa3f.js
rename to assets/contemporary-college-english-1.html-49632c93.js
index 405ed302..29bb82e2 100644
--- a/assets/contemporary-college-english-1.html-6b25fa3f.js
+++ b/assets/contemporary-college-english-1.html-49632c93.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-971cc7fe","path":"/english/contemporary-college-english-1.html","title":"现代大学英语精读(第2版)第一册","lang":"zh-CN","frontmatter":{"date":"2022-05-29T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第一册 介绍 全书链接:https://www.ximalaya.com/album/43891910 虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。 虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-1.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第一册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第一册 介绍 全书链接:https://www.ximalaya.com/album/43891910 虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。 虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-05-29T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第一册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-05-29T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"值得一读的文章","slug":"值得一读的文章","link":"#值得一读的文章","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.38,"words":414},"filePathRelative":"english/contemporary-college-english-1.md","localizedDate":"2022年5月29日","excerpt":"

现代大学英语精读(第2版)第一册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/43891910

\\n

虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。

\\n

虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-971cc7fe","path":"/english/contemporary-college-english-1.html","title":"现代大学英语精读(第2版)第一册","lang":"zh-CN","frontmatter":{"date":"2022-05-29T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第一册 介绍 全书链接:https://www.ximalaya.com/album/43891910 虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。 虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-1.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第一册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第一册 介绍 全书链接:https://www.ximalaya.com/album/43891910 虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。 虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-05-29T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第一册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-05-29T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"值得一读的文章","slug":"值得一读的文章","link":"#值得一读的文章","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.38,"words":414},"filePathRelative":"english/contemporary-college-english-1.md","localizedDate":"2022年5月29日","excerpt":"

现代大学英语精读(第2版)第一册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/43891910

\\n

虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。

\\n

虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。

","autoDesc":true}');export{e as data}; diff --git a/assets/contemporary-college-english-1.html-8191f95a.js b/assets/contemporary-college-english-1.html-e4e094da.js similarity index 98% rename from assets/contemporary-college-english-1.html-8191f95a.js rename to assets/contemporary-college-english-1.html-e4e094da.js index 88069909..cede6497 100644 --- a/assets/contemporary-college-english-1.html-8191f95a.js +++ b/assets/contemporary-college-english-1.html-e4e094da.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as a,o as s,c as l,a as e,b as t,d as n}from"./app-b649ee34.js";const h={},c=e("h1",{id:"现代大学英语精读-第2版-第一册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第一册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第一册")],-1),i=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),_={href:"https://www.ximalaya.com/album/43891910",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,"虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。",-1),u=e("p",null,"虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。",-1),m=e("h2",{id:"值得一读的文章",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#值得一读的文章","aria-hidden":"true"},"#"),t(" 值得一读的文章")],-1),p=e("p",null,"本册里面大部分是记叙文,阅读趣味性比较强。",-1),f=e("p",null,"欧亨利不愧是大师,第一册里收录了两篇他的小说。",-1),w={href:"https://www.ximalaya.com/sound/356764931",target:"_blank",rel:"noopener noreferrer"},x=e("br",null,null,-1),y={href:"https://www.ximalaya.com/sound/356764931",target:"_blank",rel:"noopener noreferrer"},b={href:"https://americanliterature.com/author/o-henry/short-story/hearts-and-hands",target:"_blank",rel:"noopener noreferrer"},g=e("br",null,null,-1),k={href:"https://americanliterature.com/author/o-henry/short-story/hearts-and-hands",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,"另外,戏剧 The Monsters Are Due in Maple Street 也值得一读",-1),v={href:"https://www.ximalaya.com/sound/357087229",target:"_blank",rel:"noopener noreferrer"},A=e("p",null,"总结一下,本册难度较低,就是来建立信心的。如果想只阅读精华,那只看我挑选出的三篇文章就够了。适应了全英文阅读后,就可以开始看第二册的内容了。",-1);function B(N,T){const r=a("ExternalLinkIcon");return s(),l("div",null,[c,i,e("p",null,[t("全书链接:"),e("a",_,[t("https://www.ximalaya.com/album/43891910"),n(r)])]),d,u,m,p,f,e("ul",null,[e("li",null,[e("a",w,[t("After Twenty Years"),n(r)]),x,t(" 经典短篇小说,应该大家都在学校看过中文版。"),e("a",y,[t("https://www.ximalaya.com/sound/356764931"),n(r)])]),e("li",null,[e("a",b,[t("Hearts And Hands"),n(r)]),g,t(" 我认为这篇小说是第一册中最有阅读难度的,可以作为阅读能力的检测,看能不能读懂。反正我第一次没读懂。本文使用了经典的结尾手法——最后一句话反转全文。"),e("a",k,[t("https://americanliterature.com/author/o-henry/short-story/hearts-and-hands"),n(r)])])]),E,e("ul",null,[e("li",null,[e("a",v,[t("https://www.ximalaya.com/sound/357087229"),n(r)])])]),A])}const H=o(h,[["render",B],["__file","contemporary-college-english-1.html.vue"]]);export{H as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as a,o as s,c as l,a as e,b as t,d as n}from"./app-a9d55428.js";const h={},c=e("h1",{id:"现代大学英语精读-第2版-第一册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第一册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第一册")],-1),i=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),_={href:"https://www.ximalaya.com/album/43891910",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,"虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。",-1),u=e("p",null,"虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。",-1),m=e("h2",{id:"值得一读的文章",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#值得一读的文章","aria-hidden":"true"},"#"),t(" 值得一读的文章")],-1),p=e("p",null,"本册里面大部分是记叙文,阅读趣味性比较强。",-1),f=e("p",null,"欧亨利不愧是大师,第一册里收录了两篇他的小说。",-1),w={href:"https://www.ximalaya.com/sound/356764931",target:"_blank",rel:"noopener noreferrer"},x=e("br",null,null,-1),y={href:"https://www.ximalaya.com/sound/356764931",target:"_blank",rel:"noopener noreferrer"},b={href:"https://americanliterature.com/author/o-henry/short-story/hearts-and-hands",target:"_blank",rel:"noopener noreferrer"},g=e("br",null,null,-1),k={href:"https://americanliterature.com/author/o-henry/short-story/hearts-and-hands",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,"另外,戏剧 The Monsters Are Due in Maple Street 也值得一读",-1),v={href:"https://www.ximalaya.com/sound/357087229",target:"_blank",rel:"noopener noreferrer"},A=e("p",null,"总结一下,本册难度较低,就是来建立信心的。如果想只阅读精华,那只看我挑选出的三篇文章就够了。适应了全英文阅读后,就可以开始看第二册的内容了。",-1);function B(N,T){const r=a("ExternalLinkIcon");return s(),l("div",null,[c,i,e("p",null,[t("全书链接:"),e("a",_,[t("https://www.ximalaya.com/album/43891910"),n(r)])]),d,u,m,p,f,e("ul",null,[e("li",null,[e("a",w,[t("After Twenty Years"),n(r)]),x,t(" 经典短篇小说,应该大家都在学校看过中文版。"),e("a",y,[t("https://www.ximalaya.com/sound/356764931"),n(r)])]),e("li",null,[e("a",b,[t("Hearts And Hands"),n(r)]),g,t(" 我认为这篇小说是第一册中最有阅读难度的,可以作为阅读能力的检测,看能不能读懂。反正我第一次没读懂。本文使用了经典的结尾手法——最后一句话反转全文。"),e("a",k,[t("https://americanliterature.com/author/o-henry/short-story/hearts-and-hands"),n(r)])])]),E,e("ul",null,[e("li",null,[e("a",v,[t("https://www.ximalaya.com/sound/357087229"),n(r)])])]),A])}const H=o(h,[["render",B],["__file","contemporary-college-english-1.html.vue"]]);export{H as default}; diff --git a/assets/contemporary-college-english-2.html-82c3bbbf.js b/assets/contemporary-college-english-2.html-47fef5cf.js similarity index 98% rename from assets/contemporary-college-english-2.html-82c3bbbf.js rename to assets/contemporary-college-english-2.html-47fef5cf.js index b89b48b1..a16108fc 100644 --- a/assets/contemporary-college-english-2.html-82c3bbbf.js +++ b/assets/contemporary-college-english-2.html-47fef5cf.js @@ -1 +1 @@ -import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as s,c as a,a as e,b as t,d as o}from"./app-b649ee34.js";const i={},h=e("h1",{id:"现代大学英语精读-第2版-第二册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第二册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第二册")],-1),c=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),_={href:"https://www.ximalaya.com/album/44290107",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,"本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。",-1),u=e("h2",{id:"值得一读的文章",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#值得一读的文章","aria-hidden":"true"},"#"),t(" 值得一读的文章")],-1),p=e("p",null,"say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂",-1),w=e("ul",null,[e("li")],-1),m={href:"https://www.missmccalister.com/uploads/3/0/9/3/30937509/lesson-7a-say-yes-by-tobias-wolff.pdf",target:"_blank",rel:"noopener noreferrer"},f={href:"https://en.wikipedia.org/wiki/Say_Yes_(short_story)",target:"_blank",rel:"noopener noreferrer"},k=e("br",null,null,-1),b={href:"https://en.wikipedia.org/wiki/Say_Yes_(short_story)",target:"_blank",rel:"noopener noreferrer"},g=e("p",null,"The Dog of Pompeii 讲述了一个盲人男孩与一只小狗的故事,令人泪目。结尾比较含蓄,思考片刻明白后,就破防了~",-1),y=e("ul",null,[e("li")],-1),x={href:"https://www.acaedu.net/cms/lib3/TX01001550/Centricity/Domain/562/Week%206%20-%20The%20Dog%20of%20Pompeii.pdf",target:"_blank",rel:"noopener noreferrer"},D=e("p",null,"button button 人心拷问:如果按下按钮,你能得到一笔钱,但世上会有一人因此而死去,你会按下按钮吗?",-1),T=e("ul",null,[e("li")],-1),v={href:"https://christian_fuller.myteachersite.org/teacher/files/documents/button%20button.pdf",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,"A Doctor's Dilemma 医生的困境",-1),C={href:"http://www.kekenet.com/daxue/201706/508290.shtml",target:"_blank",rel:"noopener noreferrer"},B={href:"https://www.gutenberg.org/files/5070/5070-h/5070-h.htm",target:"_blank",rel:"noopener noreferrer"},S=e("br",null,null,-1),W={href:"https://www.gutenberg.org/files/5070/5070-h/5070-h.htm",target:"_blank",rel:"noopener noreferrer"},A=e("p",null,"the-oyster-and-the-pearl",-1),I={href:"http://www.kekenet.com/daxue/201708/521462.shtml",target:"_blank",rel:"noopener noreferrer"},L=e("li",null,null,-1),N={href:"https://schoolworkhelper.net/william-saroyans-the-oyster-and-the-pearl-summary-analysis/",target:"_blank",rel:"noopener noreferrer"},P=e("p",null,"文册收录了两篇奥巴马的演讲,还是挺有意思的,能打动人心的部分当然有,同时也可以看出一些政治家演讲的特色:",-1),V=e("ol",null,[e("li",null,"赞美对方。糖衣炮弹过去,对方就伸手不打笑脸人"),e("li",null,"用好词,美化自己,把功劳往自己身上揽"),e("li",null,"强调双方关系: 我们是很友好的哦、我们是合作伙伴哦"),e("li",null,"回顾历史,展望未来:我们已经走了很长的路,做出了很多的改变,取得了很大的进步,为了下一代、为了未来,我们要加油鸭!")],-1),Y=e("p",null,"相关链接:",-1),M={href:"http://www.kekenet.com/daxue/201707/518237.shtml",target:"_blank",rel:"noopener noreferrer"},O={href:"http://www.kekenet.com/daxue/201708/518540.shtml",target:"_blank",rel:"noopener noreferrer"},X=e("p",null,"另外,我摘录了一些觉得不错的句子:",-1),j=e("ul",null,[e("li",null,"When dealing with people, let us remember we are not dealing with creatures of logic."),e("li",null,"We are dealing with creatures of emotion, creatures bristling with prejudices, and motivated by pride and vanity."),e("li",null,[t("Any fool can criticize, condemn and complain—and most fools do. But it takes character and self-control to be"),e("br"),t(" understanding and forgiving.")])],-1);function z(H,R){const n=r("ExternalLinkIcon");return s(),a("div",null,[h,c,e("p",null,[t("全书链接:"),e("a",_,[t("https://www.ximalaya.com/album/44290107"),o(n)])]),d,u,p,w,e("p",null,[t("原文:"),e("a",m,[t("https://www.missmccalister.com/uploads/3/0/9/3/30937509/lesson-7a-say-yes-by-tobias-wolff.pdf"),o(n)])]),e("ul",null,[e("li",null,[e("a",f,[t("wiki"),o(n)]),k,t(" 说明:"),e("a",b,[t("https://en.wikipedia.org/wiki/Say_Yes_(short_story)"),o(n)])])]),g,y,e("p",null,[t("原文:"),e("a",x,[t("https://www.acaedu.net/cms/lib3/TX01001550/Centricity/Domain/562/Week%206%20-%20The%20Dog%20of%20Pompeii.pdf"),o(n)])]),D,T,e("p",null,[t("原文:"),e("a",v,[t("https://christian_fuller.myteachersite.org/teacher/files/documents/button%20button.pdf"),o(n)])]),E,e("ul",null,[e("li",null,[t("原文:"),e("a",C,[t("http://www.kekenet.com/daxue/201706/508290.shtml"),o(n)])]),e("li",null,[t("拓展阅读,"),e("a",B,[t("肖伯纳的 THE DOCTOR’S DILEMMA"),o(n)]),S,t(" :"),e("a",W,[t("https://www.gutenberg.org/files/5070/5070-h/5070-h.htm"),o(n)])])]),A,e("ul",null,[e("li",null,[t("原文:"),e("a",I,[t("http://www.kekenet.com/daxue/201708/521462.shtml"),o(n)])]),L]),e("p",null,[t("解析:"),e("a",N,[t("https://schoolworkhelper.net/william-saroyans-the-oyster-and-the-pearl-summary-analysis/"),o(n)])]),P,V,Y,e("ul",null,[e("li",null,[t("竞选胜利演讲:"),e("a",M,[t("http://www.kekenet.com/daxue/201707/518237.shtml"),o(n)])]),e("li",null,[t("在上海的演讲:"),e("a",O,[t("http://www.kekenet.com/daxue/201708/518540.shtml"),o(n)])])]),X,j])}const G=l(i,[["render",z],["__file","contemporary-college-english-2.html.vue"]]);export{G as default}; +import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as s,c as a,a as e,b as t,d as o}from"./app-a9d55428.js";const i={},h=e("h1",{id:"现代大学英语精读-第2版-第二册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第二册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第二册")],-1),c=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),_={href:"https://www.ximalaya.com/album/44290107",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,"本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。",-1),u=e("h2",{id:"值得一读的文章",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#值得一读的文章","aria-hidden":"true"},"#"),t(" 值得一读的文章")],-1),p=e("p",null,"say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂",-1),w=e("ul",null,[e("li")],-1),m={href:"https://www.missmccalister.com/uploads/3/0/9/3/30937509/lesson-7a-say-yes-by-tobias-wolff.pdf",target:"_blank",rel:"noopener noreferrer"},f={href:"https://en.wikipedia.org/wiki/Say_Yes_(short_story)",target:"_blank",rel:"noopener noreferrer"},k=e("br",null,null,-1),b={href:"https://en.wikipedia.org/wiki/Say_Yes_(short_story)",target:"_blank",rel:"noopener noreferrer"},g=e("p",null,"The Dog of Pompeii 讲述了一个盲人男孩与一只小狗的故事,令人泪目。结尾比较含蓄,思考片刻明白后,就破防了~",-1),y=e("ul",null,[e("li")],-1),x={href:"https://www.acaedu.net/cms/lib3/TX01001550/Centricity/Domain/562/Week%206%20-%20The%20Dog%20of%20Pompeii.pdf",target:"_blank",rel:"noopener noreferrer"},D=e("p",null,"button button 人心拷问:如果按下按钮,你能得到一笔钱,但世上会有一人因此而死去,你会按下按钮吗?",-1),T=e("ul",null,[e("li")],-1),v={href:"https://christian_fuller.myteachersite.org/teacher/files/documents/button%20button.pdf",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,"A Doctor's Dilemma 医生的困境",-1),C={href:"http://www.kekenet.com/daxue/201706/508290.shtml",target:"_blank",rel:"noopener noreferrer"},B={href:"https://www.gutenberg.org/files/5070/5070-h/5070-h.htm",target:"_blank",rel:"noopener noreferrer"},S=e("br",null,null,-1),W={href:"https://www.gutenberg.org/files/5070/5070-h/5070-h.htm",target:"_blank",rel:"noopener noreferrer"},A=e("p",null,"the-oyster-and-the-pearl",-1),I={href:"http://www.kekenet.com/daxue/201708/521462.shtml",target:"_blank",rel:"noopener noreferrer"},L=e("li",null,null,-1),N={href:"https://schoolworkhelper.net/william-saroyans-the-oyster-and-the-pearl-summary-analysis/",target:"_blank",rel:"noopener noreferrer"},P=e("p",null,"文册收录了两篇奥巴马的演讲,还是挺有意思的,能打动人心的部分当然有,同时也可以看出一些政治家演讲的特色:",-1),V=e("ol",null,[e("li",null,"赞美对方。糖衣炮弹过去,对方就伸手不打笑脸人"),e("li",null,"用好词,美化自己,把功劳往自己身上揽"),e("li",null,"强调双方关系: 我们是很友好的哦、我们是合作伙伴哦"),e("li",null,"回顾历史,展望未来:我们已经走了很长的路,做出了很多的改变,取得了很大的进步,为了下一代、为了未来,我们要加油鸭!")],-1),Y=e("p",null,"相关链接:",-1),M={href:"http://www.kekenet.com/daxue/201707/518237.shtml",target:"_blank",rel:"noopener noreferrer"},O={href:"http://www.kekenet.com/daxue/201708/518540.shtml",target:"_blank",rel:"noopener noreferrer"},X=e("p",null,"另外,我摘录了一些觉得不错的句子:",-1),j=e("ul",null,[e("li",null,"When dealing with people, let us remember we are not dealing with creatures of logic."),e("li",null,"We are dealing with creatures of emotion, creatures bristling with prejudices, and motivated by pride and vanity."),e("li",null,[t("Any fool can criticize, condemn and complain—and most fools do. But it takes character and self-control to be"),e("br"),t(" understanding and forgiving.")])],-1);function z(H,R){const n=r("ExternalLinkIcon");return s(),a("div",null,[h,c,e("p",null,[t("全书链接:"),e("a",_,[t("https://www.ximalaya.com/album/44290107"),o(n)])]),d,u,p,w,e("p",null,[t("原文:"),e("a",m,[t("https://www.missmccalister.com/uploads/3/0/9/3/30937509/lesson-7a-say-yes-by-tobias-wolff.pdf"),o(n)])]),e("ul",null,[e("li",null,[e("a",f,[t("wiki"),o(n)]),k,t(" 说明:"),e("a",b,[t("https://en.wikipedia.org/wiki/Say_Yes_(short_story)"),o(n)])])]),g,y,e("p",null,[t("原文:"),e("a",x,[t("https://www.acaedu.net/cms/lib3/TX01001550/Centricity/Domain/562/Week%206%20-%20The%20Dog%20of%20Pompeii.pdf"),o(n)])]),D,T,e("p",null,[t("原文:"),e("a",v,[t("https://christian_fuller.myteachersite.org/teacher/files/documents/button%20button.pdf"),o(n)])]),E,e("ul",null,[e("li",null,[t("原文:"),e("a",C,[t("http://www.kekenet.com/daxue/201706/508290.shtml"),o(n)])]),e("li",null,[t("拓展阅读,"),e("a",B,[t("肖伯纳的 THE DOCTOR’S DILEMMA"),o(n)]),S,t(" :"),e("a",W,[t("https://www.gutenberg.org/files/5070/5070-h/5070-h.htm"),o(n)])])]),A,e("ul",null,[e("li",null,[t("原文:"),e("a",I,[t("http://www.kekenet.com/daxue/201708/521462.shtml"),o(n)])]),L]),e("p",null,[t("解析:"),e("a",N,[t("https://schoolworkhelper.net/william-saroyans-the-oyster-and-the-pearl-summary-analysis/"),o(n)])]),P,V,Y,e("ul",null,[e("li",null,[t("竞选胜利演讲:"),e("a",M,[t("http://www.kekenet.com/daxue/201707/518237.shtml"),o(n)])]),e("li",null,[t("在上海的演讲:"),e("a",O,[t("http://www.kekenet.com/daxue/201708/518540.shtml"),o(n)])])]),X,j])}const G=l(i,[["render",z],["__file","contemporary-college-english-2.html.vue"]]);export{G as default}; diff --git a/assets/contemporary-college-english-2.html-de9e2dd5.js b/assets/contemporary-college-english-2.html-675f1cf2.js similarity index 92% rename from assets/contemporary-college-english-2.html-de9e2dd5.js rename to assets/contemporary-college-english-2.html-675f1cf2.js index 0701d526..09ab222e 100644 --- a/assets/contemporary-college-english-2.html-de9e2dd5.js +++ b/assets/contemporary-college-english-2.html-675f1cf2.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-93b316c0","path":"/english/contemporary-college-english-2.html","title":"现代大学英语精读(第2版)第二册","lang":"zh-CN","frontmatter":{"date":"2022-06-04T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第二册 介绍 全书链接:https://www.ximalaya.com/album/44290107 本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。 值得一读的文章 say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-2.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第二册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第二册 介绍 全书链接:https://www.ximalaya.com/album/44290107 本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。 值得一读的文章 say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-04T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第二册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-04T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"值得一读的文章","slug":"值得一读的文章","link":"#值得一读的文章","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.96,"words":588},"filePathRelative":"english/contemporary-college-english-2.md","localizedDate":"2022年6月4日","excerpt":"

现代大学英语精读(第2版)第二册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/44290107

\\n

本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。

\\n

值得一读的文章

\\n

say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-93b316c0","path":"/english/contemporary-college-english-2.html","title":"现代大学英语精读(第2版)第二册","lang":"zh-CN","frontmatter":{"date":"2022-06-04T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第二册 介绍 全书链接:https://www.ximalaya.com/album/44290107 本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。 值得一读的文章 say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-2.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第二册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第二册 介绍 全书链接:https://www.ximalaya.com/album/44290107 本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。 值得一读的文章 say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-04T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第二册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-04T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"值得一读的文章","slug":"值得一读的文章","link":"#值得一读的文章","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.96,"words":588},"filePathRelative":"english/contemporary-college-english-2.md","localizedDate":"2022年6月4日","excerpt":"

现代大学英语精读(第2版)第二册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/44290107

\\n

本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。

\\n

值得一读的文章

\\n

say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂

","autoDesc":true}');export{e as data}; diff --git a/assets/contemporary-college-english-3.html-9d16468c.js b/assets/contemporary-college-english-3.html-06118028.js similarity index 93% rename from assets/contemporary-college-english-3.html-9d16468c.js rename to assets/contemporary-college-english-3.html-06118028.js index aad0a9b8..c4b9e646 100644 --- a/assets/contemporary-college-english-3.html-9d16468c.js +++ b/assets/contemporary-college-english-3.html-06118028.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-90496582","path":"/english/contemporary-college-english-3.html","title":"现代大学英语精读(第2版)第三册","lang":"zh-CN","frontmatter":{"date":"2022-06-25T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第三册 介绍 全书链接:https://www.ximalaya.com/album/44439108 阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-3.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第三册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第三册 介绍 全书链接:https://www.ximalaya.com/album/44439108 阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-25T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第三册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-25T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"值得一读的文章","slug":"值得一读的文章","link":"#值得一读的文章","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.95,"words":586},"filePathRelative":"english/contemporary-college-english-3.md","localizedDate":"2022年6月25日","excerpt":"

现代大学英语精读(第2版)第三册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/44439108

\\n

阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-90496582","path":"/english/contemporary-college-english-3.html","title":"现代大学英语精读(第2版)第三册","lang":"zh-CN","frontmatter":{"date":"2022-06-25T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第三册 介绍 全书链接:https://www.ximalaya.com/album/44439108 阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-3.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第三册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第三册 介绍 全书链接:https://www.ximalaya.com/album/44439108 阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-25T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第三册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-25T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"值得一读的文章","slug":"值得一读的文章","link":"#值得一读的文章","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.95,"words":586},"filePathRelative":"english/contemporary-college-english-3.md","localizedDate":"2022年6月25日","excerpt":"

现代大学英语精读(第2版)第三册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/44439108

\\n

阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。

","autoDesc":true}');export{e as data}; diff --git a/assets/contemporary-college-english-3.html-f1b95697.js b/assets/contemporary-college-english-3.html-80541a07.js similarity index 98% rename from assets/contemporary-college-english-3.html-f1b95697.js rename to assets/contemporary-college-english-3.html-80541a07.js index e8ed154d..0b090974 100644 --- a/assets/contemporary-college-english-3.html-f1b95697.js +++ b/assets/contemporary-college-english-3.html-80541a07.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as s,c as a,a as e,b as t,d as l}from"./app-b649ee34.js";const i={},h=e("h1",{id:"现代大学英语精读-第2版-第三册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第三册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第三册")],-1),_=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),c={href:"https://www.ximalaya.com/album/44439108",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,"阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。",-1),p=e("p",null,"这种转变,说明了两点:",-1),m=e("ol",null,[e("li",null,"我开始应用我在中文阅读中培养出来的阅读喜爱,去评判英文文章了"),e("li",null,"我开始有意识地运用阅读技巧,恰当地泛读,不再是拿到一篇文章就逐字逐句地读")],-1),u=e("h2",{id:"值得一读的文章",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#值得一读的文章","aria-hidden":"true"},"#"),t(" 值得一读的文章")],-1),w=e("p",null,"A-DILL-PICKLE 讲述的是曾经的恋人偶遇,欧亨利式结尾:",-1),f=e("ul",null,[e("li")],-1),k={href:"https://www.katherinemansfieldsociety.org/archive/www.katherinemansfieldsociety.org/assets/KM-Stories/A-DILL-PICKLE1917.pdf",target:"_blank",rel:"noopener noreferrer"},g=e("br",null,null,-1),b={href:"https://en.wikipedia.org/wiki/A_Dill_Pickle#Plot_summary",target:"_blank",rel:"noopener noreferrer"},y=e("p",null,"The Invisible Japanese Gentlemen",-1),x=e("ul",null,[e("li")],-1),v={href:"https://www.ff.umb.sk/app/cmsFile.php?disposition=a&ID=4292",target:"_blank",rel:"noopener noreferrer"},L={href:"http://sittingbee.com/the-invisible-japanese-gentlemen-graham-greene/",target:"_blank",rel:"noopener noreferrer"},I=e("p",null,"My Grandmother, the Bag Lady 被这篇文章打动,泪目了",-1),D={href:"http://www.kekenet.com/daxue/201704/504622.shtml",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,"The End of the Civil War 本册收录了挺多美国南北战争相关的内容,这是其中角度新颖的一篇",-1),C={href:"http://www.kekenet.com/daxue/201705/509083.shtml",target:"_blank",rel:"noopener noreferrer"},P=e("p",null,"Why Historians Disagree 科普了一下历史学是如何看待历史,有助于我们更理性地学习历史",-1),A={href:"https://www.ximalaya.com/sound/361971944",target:"_blank",rel:"noopener noreferrer"},B=e("p",null,"the-most-dangerous-game 可以说是三册中最精彩的一篇小说之一,不容错过",-1),K=e("ul",null,[e("li")],-1),T={href:"https://www.litcharts.com/lit/the-most-dangerous-game/summary-and-analysis",target:"_blank",rel:"noopener noreferrer"},M=e("p",null,"The Bench 用一个黑人小哥故意坐在标明只能由欧洲人坐的板凳上的故事,以小见大,描绘了 Civil War 之后美国种族歧视现状",-1),N={href:"http://www.kekenet.com/daxue/201708/521739.shtml",target:"_blank",rel:"noopener noreferrer"},V=e("p",null,"Twelve Angry Men 十二怒汉,是本册的精彩内容之一。激发了对法治话题的兴趣,看得我直接睡不着,接着去B站看电影版。",-1),W={href:"http://www.kekenet.com/daxue/201708/522771.shtml",target:"_blank",rel:"noopener noreferrer"},j=e("li",null,null,-1),F={href:"https://www.bilibili.com/bangumi/play/ep332629?theme=movie&spm_id_from=333.337.0.0",target:"_blank",rel:"noopener noreferrer"};function G(S,H){const n=r("ExternalLinkIcon");return s(),a("div",null,[h,_,e("p",null,[t("全书链接:"),e("a",c,[t("https://www.ximalaya.com/album/44439108"),l(n)])]),d,p,m,u,w,f,e("p",null,[t("原文:"),e("a",k,[t("https://www.katherinemansfieldsociety.org/archive/www.katherinemansfieldsociety.org/assets/KM-Stories/A-DILL-PICKLE1917.pdf"),l(n)])]),e("ul",null,[e("li",null,[t("wiki"),g,t(" 解析:"),e("a",b,[t("https://en.wikipedia.org/wiki/A_Dill_Pickle#Plot_summary"),l(n)])])]),y,x,e("p",null,[t("原文:"),e("a",v,[t("https://www.ff.umb.sk/app/cmsFile.php?disposition=a&ID=4292"),l(n)])]),e("ul",null,[e("li",null,[e("a",L,[t("分析:http://sittingbee.com/the-invisible-japanese-gentlemen-graham-greene/"),l(n)])])]),I,e("ul",null,[e("li",null,[t("原文:"),e("a",D,[t("http://www.kekenet.com/daxue/201704/504622.shtml"),l(n)])])]),E,e("ul",null,[e("li",null,[t("原文:"),e("a",C,[t("http://www.kekenet.com/daxue/201705/509083.shtml"),l(n)])])]),P,e("ul",null,[e("li",null,[t("原文:"),e("a",A,[t("https://www.ximalaya.com/sound/361971944"),l(n)])])]),B,K,e("p",null,[t("原文+分析(左边是内容,右边是分析):"),e("a",T,[t("https://www.litcharts.com/lit/the-most-dangerous-game/summary-and-analysis"),l(n)])]),M,e("ul",null,[e("li",null,[t("原文:"),e("a",N,[t("http://www.kekenet.com/daxue/201708/521739.shtml"),l(n)])])]),V,e("ul",null,[e("li",null,[t("原文:"),e("a",W,[t("http://www.kekenet.com/daxue/201708/522771.shtml"),l(n)])]),j]),e("p",null,[t("电影:"),e("a",F,[t("https://www.bilibili.com/bangumi/play/ep332629?theme=movie"),l(n)])])])}const z=o(i,[["render",G],["__file","contemporary-college-english-3.html.vue"]]);export{z as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as s,c as a,a as e,b as t,d as l}from"./app-a9d55428.js";const i={},h=e("h1",{id:"现代大学英语精读-第2版-第三册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第三册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第三册")],-1),_=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),c={href:"https://www.ximalaya.com/album/44439108",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,"阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。",-1),p=e("p",null,"这种转变,说明了两点:",-1),m=e("ol",null,[e("li",null,"我开始应用我在中文阅读中培养出来的阅读喜爱,去评判英文文章了"),e("li",null,"我开始有意识地运用阅读技巧,恰当地泛读,不再是拿到一篇文章就逐字逐句地读")],-1),u=e("h2",{id:"值得一读的文章",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#值得一读的文章","aria-hidden":"true"},"#"),t(" 值得一读的文章")],-1),w=e("p",null,"A-DILL-PICKLE 讲述的是曾经的恋人偶遇,欧亨利式结尾:",-1),f=e("ul",null,[e("li")],-1),k={href:"https://www.katherinemansfieldsociety.org/archive/www.katherinemansfieldsociety.org/assets/KM-Stories/A-DILL-PICKLE1917.pdf",target:"_blank",rel:"noopener noreferrer"},g=e("br",null,null,-1),b={href:"https://en.wikipedia.org/wiki/A_Dill_Pickle#Plot_summary",target:"_blank",rel:"noopener noreferrer"},y=e("p",null,"The Invisible Japanese Gentlemen",-1),x=e("ul",null,[e("li")],-1),v={href:"https://www.ff.umb.sk/app/cmsFile.php?disposition=a&ID=4292",target:"_blank",rel:"noopener noreferrer"},L={href:"http://sittingbee.com/the-invisible-japanese-gentlemen-graham-greene/",target:"_blank",rel:"noopener noreferrer"},I=e("p",null,"My Grandmother, the Bag Lady 被这篇文章打动,泪目了",-1),D={href:"http://www.kekenet.com/daxue/201704/504622.shtml",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,"The End of the Civil War 本册收录了挺多美国南北战争相关的内容,这是其中角度新颖的一篇",-1),C={href:"http://www.kekenet.com/daxue/201705/509083.shtml",target:"_blank",rel:"noopener noreferrer"},P=e("p",null,"Why Historians Disagree 科普了一下历史学是如何看待历史,有助于我们更理性地学习历史",-1),A={href:"https://www.ximalaya.com/sound/361971944",target:"_blank",rel:"noopener noreferrer"},B=e("p",null,"the-most-dangerous-game 可以说是三册中最精彩的一篇小说之一,不容错过",-1),K=e("ul",null,[e("li")],-1),T={href:"https://www.litcharts.com/lit/the-most-dangerous-game/summary-and-analysis",target:"_blank",rel:"noopener noreferrer"},M=e("p",null,"The Bench 用一个黑人小哥故意坐在标明只能由欧洲人坐的板凳上的故事,以小见大,描绘了 Civil War 之后美国种族歧视现状",-1),N={href:"http://www.kekenet.com/daxue/201708/521739.shtml",target:"_blank",rel:"noopener noreferrer"},V=e("p",null,"Twelve Angry Men 十二怒汉,是本册的精彩内容之一。激发了对法治话题的兴趣,看得我直接睡不着,接着去B站看电影版。",-1),W={href:"http://www.kekenet.com/daxue/201708/522771.shtml",target:"_blank",rel:"noopener noreferrer"},j=e("li",null,null,-1),F={href:"https://www.bilibili.com/bangumi/play/ep332629?theme=movie&spm_id_from=333.337.0.0",target:"_blank",rel:"noopener noreferrer"};function G(S,H){const n=r("ExternalLinkIcon");return s(),a("div",null,[h,_,e("p",null,[t("全书链接:"),e("a",c,[t("https://www.ximalaya.com/album/44439108"),l(n)])]),d,p,m,u,w,f,e("p",null,[t("原文:"),e("a",k,[t("https://www.katherinemansfieldsociety.org/archive/www.katherinemansfieldsociety.org/assets/KM-Stories/A-DILL-PICKLE1917.pdf"),l(n)])]),e("ul",null,[e("li",null,[t("wiki"),g,t(" 解析:"),e("a",b,[t("https://en.wikipedia.org/wiki/A_Dill_Pickle#Plot_summary"),l(n)])])]),y,x,e("p",null,[t("原文:"),e("a",v,[t("https://www.ff.umb.sk/app/cmsFile.php?disposition=a&ID=4292"),l(n)])]),e("ul",null,[e("li",null,[e("a",L,[t("分析:http://sittingbee.com/the-invisible-japanese-gentlemen-graham-greene/"),l(n)])])]),I,e("ul",null,[e("li",null,[t("原文:"),e("a",D,[t("http://www.kekenet.com/daxue/201704/504622.shtml"),l(n)])])]),E,e("ul",null,[e("li",null,[t("原文:"),e("a",C,[t("http://www.kekenet.com/daxue/201705/509083.shtml"),l(n)])])]),P,e("ul",null,[e("li",null,[t("原文:"),e("a",A,[t("https://www.ximalaya.com/sound/361971944"),l(n)])])]),B,K,e("p",null,[t("原文+分析(左边是内容,右边是分析):"),e("a",T,[t("https://www.litcharts.com/lit/the-most-dangerous-game/summary-and-analysis"),l(n)])]),M,e("ul",null,[e("li",null,[t("原文:"),e("a",N,[t("http://www.kekenet.com/daxue/201708/521739.shtml"),l(n)])])]),V,e("ul",null,[e("li",null,[t("原文:"),e("a",W,[t("http://www.kekenet.com/daxue/201708/522771.shtml"),l(n)])]),j]),e("p",null,[t("电影:"),e("a",F,[t("https://www.bilibili.com/bangumi/play/ep332629?theme=movie"),l(n)])])])}const z=o(i,[["render",G],["__file","contemporary-college-english-3.html.vue"]]);export{z as default}; diff --git a/assets/contemporary-college-english-4.html-ab55347b.js b/assets/contemporary-college-english-4.html-5a5508d7.js similarity index 90% rename from assets/contemporary-college-english-4.html-ab55347b.js rename to assets/contemporary-college-english-4.html-5a5508d7.js index bf80e4d9..d7942421 100644 --- a/assets/contemporary-college-english-4.html-ab55347b.js +++ b/assets/contemporary-college-english-4.html-5a5508d7.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-8cdfb444","path":"/english/contemporary-college-english-4.html","title":"现代大学英语精读(第2版)第四册","lang":"zh-CN","frontmatter":{"date":"2022-07-11T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第四册 介绍 全书链接:https://www.ximalaya.com/album/44641280 本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。 值得一读的文章 Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-4.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第四册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第四册 介绍 全书链接:https://www.ximalaya.com/album/44641280 本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。 值得一读的文章 Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-07-11T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第四册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-07-11T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"值得一读的文章","slug":"值得一读的文章","link":"#值得一读的文章","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.02,"words":607},"filePathRelative":"english/contemporary-college-english-4.md","localizedDate":"2022年7月11日","excerpt":"

现代大学英语精读(第2版)第四册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/44641280

\\n

本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。

\\n

值得一读的文章

\\n

Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-8cdfb444","path":"/english/contemporary-college-english-4.html","title":"现代大学英语精读(第2版)第四册","lang":"zh-CN","frontmatter":{"date":"2022-07-11T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第四册 介绍 全书链接:https://www.ximalaya.com/album/44641280 本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。 值得一读的文章 Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-4.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第四册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第四册 介绍 全书链接:https://www.ximalaya.com/album/44641280 本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。 值得一读的文章 Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-07-11T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第四册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-07-11T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"值得一读的文章","slug":"值得一读的文章","link":"#值得一读的文章","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.02,"words":607},"filePathRelative":"english/contemporary-college-english-4.md","localizedDate":"2022年7月11日","excerpt":"

现代大学英语精读(第2版)第四册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/44641280

\\n

本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。

\\n

值得一读的文章

\\n

Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从

","autoDesc":true}');export{e as data}; diff --git a/assets/contemporary-college-english-4.html-bc05713f.js b/assets/contemporary-college-english-4.html-756c6e8b.js similarity index 98% rename from assets/contemporary-college-english-4.html-bc05713f.js rename to assets/contemporary-college-english-4.html-756c6e8b.js index 26bea366..af15d475 100644 --- a/assets/contemporary-college-english-4.html-bc05713f.js +++ b/assets/contemporary-college-english-4.html-756c6e8b.js @@ -1 +1 @@ -import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as a,c as s,a as e,b as t,d as o}from"./app-b649ee34.js";const h={},u=e("h1",{id:"现代大学英语精读-第2版-第四册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第四册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第四册")],-1),c=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),d={href:"https://www.ximalaya.com/album/44641280",target:"_blank",rel:"noopener noreferrer"},i=e("p",null,"本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。",-1),_=e("h2",{id:"值得一读的文章",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#值得一读的文章","aria-hidden":"true"},"#"),t(" 值得一读的文章")],-1),w=e("p",null,"Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从",-1),p={href:"http://www.kekenet.com/daxue/201808/561902.shtml",target:"_blank",rel:"noopener noreferrer"},m=e("p",null,"Economic Growth Is a Path to Perdition, Not Prosperity 提醒我们更加理性地看待“增长”",-1),f={href:"http://www.kekenet.com/daxue/201811/571277.shtml",target:"_blank",rel:"noopener noreferrer"},k=e("p",null,"The Damned Human Race 其实我觉得本文并非“值得一读”,但还列举出来原因有二:一是毕竟是马克·吐温之作,不可怠慢;二是我认为其论点新颖,不愧为讽刺大师,但论据越到后面越无说服力,可列为“反面教材”",-1),x={href:"http://www.kekenet.com/daxue/201812/573461.shtml",target:"_blank",rel:"noopener noreferrer"},g=e("p",null,"A String of Beads 挺有趣的文章,揭示了人们普遍的一种心理:事情的真相相比,人们更在意的是故事的戏剧性,或是否符合自己心中的设定",-1),b={href:"http://www.kekenet.com/daxue/201812/574998.shtml",target:"_blank",rel:"noopener noreferrer"},y=e("p",null,"The World House 马丁·路德·金 的文章",-1),F={href:"http://www.kekenet.com/daxue/201901/576160.shtml",target:"_blank",rel:"noopener noreferrer"},v=e("p",null,"Soldier's Heart 也叫PTSD,由战后士兵自述",-1),E={href:"http://www.kekenet.com/daxue/201902/578424.shtml",target:"_blank",rel:"noopener noreferrer"},C=e("p",null,"Secrets 这是一篇可以让人由好奇变为感动的文章。我原以为讲的是主人公的 “aunt” 与其旧情人的故事,直到最后,“forgive”一词的出现,令我恍然大悟,瞬间泪目。为避免剧透,我不细说了,强烈推荐。",-1),T={href:"http://www.kekenet.com/daxue/201903/580020.shtml",target:"_blank",rel:"noopener noreferrer"},q=e("p",null,[t("The Rivals"),e("br"),t(" 两个男的在言语上较劲,只觉得好笑,可看到后面,我心情如此图"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1688258250066.png",alt:"1657000860176_9DFDD7F8-C809-4aa4-A996-05F4C984C76A.png",loading:"lazy"}),e("br"),t(" 强烈推荐,你一定也会“surprised”!")],-1),B={href:"http://www.kekenet.com/daxue/201904/584021.shtml",target:"_blank",rel:"noopener noreferrer"},D=e("p",null,"Cord:一对母女的故事",-1),N={href:"https://www.ximalaya.com/sound/364021411",target:"_blank",rel:"noopener noreferrer"},A=e("li",null,null,-1),H={href:"https://www.yuque.com/office/yuque/0/2022/pdf/160590/1657283857640-11aa59bb-edc1-475f-a97f-37124a27afd9.pdf?from=https%3A%2F%2Fwww.yuque.com%2Flevy%2Fblog%2Fxweufx%2Fedit",target:"_blank",rel:"noopener noreferrer"},I=e("p",null,"The Never-Ending Fight “机器人学之父”阿西莫夫的反迷信文章",-1),P={href:"https://www.ximalaya.com/sound/364032034",target:"_blank",rel:"noopener noreferrer"},S=e("p",null,"Man of the Moment 又到了令人享受的戏剧欣赏时间",-1),V={href:"https://www.ximalaya.com/sound/364032690",target:"_blank",rel:"noopener noreferrer"},G=e("p",null,"Is Everybody Happy 讨论了幸福的定义",-1),L={href:"https://www.ximalaya.com/sound/364033142",target:"_blank",rel:"noopener noreferrer"},M={href:"https://americanliterature.com/author/o-henry/short-story/hearts-and-hands",target:"_blank",rel:"noopener noreferrer"},R=e("br",null,null,-1);function z(W,j){const n=r("ExternalLinkIcon");return a(),s("div",null,[u,c,e("p",null,[t("全书链接:"),e("a",d,[t("https://www.ximalaya.com/album/44641280"),o(n)])]),i,_,w,e("ul",null,[e("li",null,[t("原文:"),e("a",p,[t("http://www.kekenet.com/daxue/201808/561902.shtml"),o(n)])])]),m,e("ul",null,[e("li",null,[t("原文:"),e("a",f,[t("http://www.kekenet.com/daxue/201811/571277.shtml"),o(n)])])]),k,e("ul",null,[e("li",null,[t("原文:"),e("a",x,[t("http://www.kekenet.com/daxue/201812/573461.shtml"),o(n)])])]),g,e("ul",null,[e("li",null,[t("原文:"),e("a",b,[t("http://www.kekenet.com/daxue/201812/574998.shtml"),o(n)])])]),y,e("ul",null,[e("li",null,[t("原文:"),e("a",F,[t("http://www.kekenet.com/daxue/201901/576160.shtml"),o(n)])])]),v,e("ul",null,[e("li",null,[t("原文:"),e("a",E,[t("http://www.kekenet.com/daxue/201902/578424.shtml"),o(n)])])]),C,e("ul",null,[e("li",null,[t("原文:"),e("a",T,[t("http://www.kekenet.com/daxue/201903/580020.shtml"),o(n)])])]),q,e("ul",null,[e("li",null,[t("原文:"),e("a",B,[t("http://www.kekenet.com/daxue/201904/584021.shtml"),o(n)])])]),D,e("ul",null,[e("li",null,[t("原文:"),e("a",N,[t("https://www.ximalaya.com/sound/364021411"),o(n)])]),A]),e("p",null,[t("论文分析。有一定难度,因为它是综合了同一个作者的三篇文章进行分析,需要挑选着读:"),e("a",H,[t("https://www.yuque.com/office/yuque/0/2022/pdf/160590/1657283857640-11aa59bb-edc1-475f-a97f-37124a27afd9.pdf"),o(n)])]),I,e("ul",null,[e("li",null,[t("原文:"),e("a",P,[t("https://www.ximalaya.com/sound/364032034"),o(n)])])]),S,e("ul",null,[e("li",null,[t("原文:"),e("a",V,[t("https://www.ximalaya.com/sound/364032690"),o(n)])])]),G,e("ul",null,[e("li",null,[e("p",null,[t("原文:"),e("a",L,[t("https://www.ximalaya.com/sound/364033142"),o(n)])]),e("p",null,[e("a",M,[R,o(n)])])])])])}const O=l(h,[["render",z],["__file","contemporary-college-english-4.html.vue"]]);export{O as default}; +import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as a,c as s,a as e,b as t,d as o}from"./app-a9d55428.js";const h={},u=e("h1",{id:"现代大学英语精读-第2版-第四册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第四册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第四册")],-1),c=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),d={href:"https://www.ximalaya.com/album/44641280",target:"_blank",rel:"noopener noreferrer"},i=e("p",null,"本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。",-1),_=e("h2",{id:"值得一读的文章",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#值得一读的文章","aria-hidden":"true"},"#"),t(" 值得一读的文章")],-1),w=e("p",null,"Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从",-1),p={href:"http://www.kekenet.com/daxue/201808/561902.shtml",target:"_blank",rel:"noopener noreferrer"},m=e("p",null,"Economic Growth Is a Path to Perdition, Not Prosperity 提醒我们更加理性地看待“增长”",-1),f={href:"http://www.kekenet.com/daxue/201811/571277.shtml",target:"_blank",rel:"noopener noreferrer"},k=e("p",null,"The Damned Human Race 其实我觉得本文并非“值得一读”,但还列举出来原因有二:一是毕竟是马克·吐温之作,不可怠慢;二是我认为其论点新颖,不愧为讽刺大师,但论据越到后面越无说服力,可列为“反面教材”",-1),x={href:"http://www.kekenet.com/daxue/201812/573461.shtml",target:"_blank",rel:"noopener noreferrer"},g=e("p",null,"A String of Beads 挺有趣的文章,揭示了人们普遍的一种心理:事情的真相相比,人们更在意的是故事的戏剧性,或是否符合自己心中的设定",-1),b={href:"http://www.kekenet.com/daxue/201812/574998.shtml",target:"_blank",rel:"noopener noreferrer"},y=e("p",null,"The World House 马丁·路德·金 的文章",-1),F={href:"http://www.kekenet.com/daxue/201901/576160.shtml",target:"_blank",rel:"noopener noreferrer"},v=e("p",null,"Soldier's Heart 也叫PTSD,由战后士兵自述",-1),E={href:"http://www.kekenet.com/daxue/201902/578424.shtml",target:"_blank",rel:"noopener noreferrer"},C=e("p",null,"Secrets 这是一篇可以让人由好奇变为感动的文章。我原以为讲的是主人公的 “aunt” 与其旧情人的故事,直到最后,“forgive”一词的出现,令我恍然大悟,瞬间泪目。为避免剧透,我不细说了,强烈推荐。",-1),T={href:"http://www.kekenet.com/daxue/201903/580020.shtml",target:"_blank",rel:"noopener noreferrer"},q=e("p",null,[t("The Rivals"),e("br"),t(" 两个男的在言语上较劲,只觉得好笑,可看到后面,我心情如此图"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1688258250066.png",alt:"1657000860176_9DFDD7F8-C809-4aa4-A996-05F4C984C76A.png",loading:"lazy"}),e("br"),t(" 强烈推荐,你一定也会“surprised”!")],-1),B={href:"http://www.kekenet.com/daxue/201904/584021.shtml",target:"_blank",rel:"noopener noreferrer"},D=e("p",null,"Cord:一对母女的故事",-1),N={href:"https://www.ximalaya.com/sound/364021411",target:"_blank",rel:"noopener noreferrer"},A=e("li",null,null,-1),H={href:"https://www.yuque.com/office/yuque/0/2022/pdf/160590/1657283857640-11aa59bb-edc1-475f-a97f-37124a27afd9.pdf?from=https%3A%2F%2Fwww.yuque.com%2Flevy%2Fblog%2Fxweufx%2Fedit",target:"_blank",rel:"noopener noreferrer"},I=e("p",null,"The Never-Ending Fight “机器人学之父”阿西莫夫的反迷信文章",-1),P={href:"https://www.ximalaya.com/sound/364032034",target:"_blank",rel:"noopener noreferrer"},S=e("p",null,"Man of the Moment 又到了令人享受的戏剧欣赏时间",-1),V={href:"https://www.ximalaya.com/sound/364032690",target:"_blank",rel:"noopener noreferrer"},G=e("p",null,"Is Everybody Happy 讨论了幸福的定义",-1),L={href:"https://www.ximalaya.com/sound/364033142",target:"_blank",rel:"noopener noreferrer"},M={href:"https://americanliterature.com/author/o-henry/short-story/hearts-and-hands",target:"_blank",rel:"noopener noreferrer"},R=e("br",null,null,-1);function z(W,j){const n=r("ExternalLinkIcon");return a(),s("div",null,[u,c,e("p",null,[t("全书链接:"),e("a",d,[t("https://www.ximalaya.com/album/44641280"),o(n)])]),i,_,w,e("ul",null,[e("li",null,[t("原文:"),e("a",p,[t("http://www.kekenet.com/daxue/201808/561902.shtml"),o(n)])])]),m,e("ul",null,[e("li",null,[t("原文:"),e("a",f,[t("http://www.kekenet.com/daxue/201811/571277.shtml"),o(n)])])]),k,e("ul",null,[e("li",null,[t("原文:"),e("a",x,[t("http://www.kekenet.com/daxue/201812/573461.shtml"),o(n)])])]),g,e("ul",null,[e("li",null,[t("原文:"),e("a",b,[t("http://www.kekenet.com/daxue/201812/574998.shtml"),o(n)])])]),y,e("ul",null,[e("li",null,[t("原文:"),e("a",F,[t("http://www.kekenet.com/daxue/201901/576160.shtml"),o(n)])])]),v,e("ul",null,[e("li",null,[t("原文:"),e("a",E,[t("http://www.kekenet.com/daxue/201902/578424.shtml"),o(n)])])]),C,e("ul",null,[e("li",null,[t("原文:"),e("a",T,[t("http://www.kekenet.com/daxue/201903/580020.shtml"),o(n)])])]),q,e("ul",null,[e("li",null,[t("原文:"),e("a",B,[t("http://www.kekenet.com/daxue/201904/584021.shtml"),o(n)])])]),D,e("ul",null,[e("li",null,[t("原文:"),e("a",N,[t("https://www.ximalaya.com/sound/364021411"),o(n)])]),A]),e("p",null,[t("论文分析。有一定难度,因为它是综合了同一个作者的三篇文章进行分析,需要挑选着读:"),e("a",H,[t("https://www.yuque.com/office/yuque/0/2022/pdf/160590/1657283857640-11aa59bb-edc1-475f-a97f-37124a27afd9.pdf"),o(n)])]),I,e("ul",null,[e("li",null,[t("原文:"),e("a",P,[t("https://www.ximalaya.com/sound/364032034"),o(n)])])]),S,e("ul",null,[e("li",null,[t("原文:"),e("a",V,[t("https://www.ximalaya.com/sound/364032690"),o(n)])])]),G,e("ul",null,[e("li",null,[e("p",null,[t("原文:"),e("a",L,[t("https://www.ximalaya.com/sound/364033142"),o(n)])]),e("p",null,[e("a",M,[R,o(n)])])])])])}const O=l(h,[["render",z],["__file","contemporary-college-english-4.html.vue"]]);export{O as default}; diff --git a/assets/contemporary-college-english-5.html-b75c916f.js b/assets/contemporary-college-english-5.html-5e519758.js similarity index 99% rename from assets/contemporary-college-english-5.html-b75c916f.js rename to assets/contemporary-college-english-5.html-5e519758.js index 5375189c..c6b105d4 100644 --- a/assets/contemporary-college-english-5.html-b75c916f.js +++ b/assets/contemporary-college-english-5.html-5e519758.js @@ -1 +1 @@ -import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o as s,c as h,a as e,b as t,d as a,f as n}from"./app-b649ee34.js";const l={},d=e("h1",{id:"现代大学英语精读-第2版-第五册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第五册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第五册")],-1),u=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),c={href:"https://www.ximalaya.com/album/49466046",target:"_blank",rel:"noopener noreferrer"},m=e("p",null,"本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。",-1),p=e("h2",{id:"who-are-you-and-what-are-you-doing-here",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#who-are-you-and-what-are-you-doing-here","aria-hidden":"true"},"#"),t(" Who Are You and What Are You Doing Here")],-1),y={href:"https://m.kekenet.com/daxue/201909/593904.shtml",target:"_blank",rel:"noopener noreferrer"},f=n('

本文分享了作者教育观,并阐述了上大学的意义,否定了上学是为了就业的观点。

摘录如下: Education has one salient enemy in present-day America, and that enemy is education——university education in
particular. To almost every university education is a means to an end. For students, that end is a good job.

The best reason to read great writers is to see if they may know you better than you know yourself. You may find your
own suppressed and rejected thoughts flowing back to you with an "alienated majesty." Reading the great writers, you may
have the experience that Longinus associated with the sublime: You feel that you have actually created the text
yourself. For somehow your predecessors are more yourself than you are.

Trying to figure out whether the stuff you're reading is true or false and being open to having your life changed is a
fraught, controversial activity. Doing so requires energy from the professors. This kind of perspective-altering
teaching and learning can cause the things which administrators fear above all else: trouble, arguments, bad press, etc.

Two Kinds

',5),w={href:"https://m.kekenet.com/daxue/201909/595380.shtml",target:"_blank",rel:"noopener noreferrer"},b=e("p",null,"本文以女儿的视角讲述了母亲望女成凤而女儿叛逆的故事,文中母亲代表的中国式家庭教育观,我想不少人会感同身受。用现在的眼光看来,这只不过是一段充满了失败的沟通、无法双赢的家庭教育经历,借助非暴力沟通或双赢思维是能够避免的。",-1),g=e("p",null,[t("摘录如下: I didn't budge. And then I decided. I didn't have to do what my mother said anymore. I wasn't her slave. This"),e("br"),t(" wasn't China. I had listened to her before and look what happened. She was the stupid one.")],-1),_=e("p",null,`You want me to be someone that I'm not!" I sobbed. "I will never be the kind of daughter you want me to be!"`,-1),k=e("p",null,[t('"Only two kinds of daughters," she shouted in Chinese. "Those who are obedient and those who follow their own mind! Only'),e("br"),t(' one kind of daughter can live in this house. Obedient daughter!"')],-1),v=e("p",null,`"Then I wish I wasn't your daughter, I wish you weren't my mother," I shouted.`,-1),x=e("h2",{id:"love-is-a-fallacy",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#love-is-a-fallacy","aria-hidden":"true"},"#"),t(" Love is a Fallacy")],-1),I={href:"https://www.ximalaya.com/sound/414753193",target:"_blank",rel:"noopener noreferrer"},T=e("p",null,"这多篇看下来,这篇是目前最有意思、最有趣、最令人忍俊不禁的。为避免剧透,我就少说一点。本文首尾响应,完美闭环,看完后我直呼我直呼,哈哈哈哈。就说到这了,总之墙裂推荐。",-1),A=e("h2",{id:"rewriting-american-history",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#rewriting-american-history","aria-hidden":"true"},"#"),t(" Rewriting American History")],-1),Y={href:"https://www.ximalaya.com/sound/414754775",target:"_blank",rel:"noopener noreferrer"},j=e("p",null,"本文讲述的是美国历史教材的一些变化及背后的思考。我认可其中的一个做法:不是对历史给一个所谓正统的结论,而是充分准备材料,从不同的角度陈述观点,引导人思考。",-1),S=e("h2",{id:"nobel-peace-price-about-global-warming",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#nobel-peace-price-about-global-warming","aria-hidden":"true"},"#"),t(" Nobel Peace Price About Global Warming")],-1),q={href:"https://www.ximalaya.com/sound/414756339",target:"_blank",rel:"noopener noreferrer"},N=e("h2",{id:"the-bluest-eyes",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-bluest-eyes","aria-hidden":"true"},"#"),t(" The Bluest Eyes")],-1),B={href:"https://www.ximalaya.com/sound/414757228",target:"_blank",rel:"noopener noreferrer"},D=e("p",null,"这是个记叙文,然而,我认为确是这么多篇文章中,最难懂的。",-1),O=e("h2",{id:"how-news-becomes-options-and-opinions-off-limits",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#how-news-becomes-options-and-opinions-off-limits","aria-hidden":"true"},"#"),t(" How News Becomes Options and Opinions Off-Limits")],-1),F={href:"https://www.ximalaya.com/sound/414759448",target:"_blank",rel:"noopener noreferrer"},L=e("p",null,"本文说明了当代一种社会现代:人们在看新闻,不是在看“事实”,而是在看“观点”,作者认为,现在的新闻跟小说并非没有相似之处了。然而,作者对这种现象并非完全持否定态度,他认为这是言论自由的一种方式。最后,作者还表达了在自由社会对“尊重”一词的看法。",-1),W=e("p",null,[t("摘录如下: In any version of a free society, the value of free speech must rank the highest, for that is the freedom without"),e("br"),t(" which all other freedoms would fall.")],-1),C=e("p",null,[t("In free societies, you must have the free play of ideas. There must be arguments, and it must be impassioned and"),e("br"),t(" untrammeled.")],-1),E=e("p",null,[t("A free society is not a calm and eventless place——that is the kind of static, dead society dictators try to create. Free"),e("br"),t(" societies are dynamic, noisy, turbulent and full of radical disagreements.")],-1),z=e("h2",{id:"the-indispensable-opposition",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-indispensable-opposition","aria-hidden":"true"},"#"),t(" The Indispensable Opposition")],-1),H={href:"https://m.kekenet.com/daxue/202001/603745.shtml",target:"_blank",rel:"noopener noreferrer"},R=n('

本文所表达的观点,属于民主与自由思想的经典内容:不可缺少的反对派(或忠诚的反对派)。

摘录如下: I wholly disapprove of what you say, but will defend to the death your right to say it.

If we truly wish to understand why freedom is necessary in a civilized society, we must begin by realizing that, because
freedom of discussion improves our own opinions, the liberties of other men are our own vital necessity.

We must insist that free oratory is only the beginning of free speech; it is not the end, but a means to an end. The end
is to find the truth.

The only reason for dwelling on all this is that if we are to preserve democracy we must understand its principles. And
the principle which distinguishes it from all other forms of government is that in a democracy the opposition not only
is tolerated as constitutional but must be maintained because it is in fact indispensable.

The democratic system cannot be operated without effective opposition. For, in making the great experiment of governing
people by consent rather than by coercion, it is not sufficient that the party in power should have a majority. It is
just as necessary that the party in power should never outrage the minority. That means that it must listen to the
minority and be moved by the criticisms of the minority. That means that its measures must take account of the
minority's objections, and that in administering measures it must remember that the minority may become the majority.

The Danger of a Single Story

',7),P={href:"https://www.ximalaya.com/sound/414765357",target:"_blank",rel:"noopener noreferrer"},V=n('

认识到硬币有正反面,并坚持凡事至少从两个角度去看待与分析,那么本篇文章就没白读。

摘录如下: It is impossible to talk about the single story without talking about power. Power is the ability not just to tell
the story of another person, but to make it the definitive story of that person.

All of these stories make me who l am. But to insist on only these negative stories is to flatten my experience and to
overlook the many other stories that formed me.

The single story creates stereotypes,and the problem with stereotypes is not that theyare untrue, but that they are
incomplete. They make one story become the only story.

The consequence of the single story is this: It robs people of dignity. It makes our recognition of our equal humanity
difficult. It emphasizes how we are different rather than how we are similar.

Come Rain or Come Shine

',6),G={href:"https://www.ximalaya.com/sound/414767340",target:"_blank",rel:"noopener noreferrer"},J=e("p",null,"看得不太懂,但故事倒挺有意思的。",-1),K={href:"https://medium.com/the-afterglow/come-rain-or-come-shine-by-kazuo-ishiguro-finding-the-moment-b09e418652db",target:"_blank",rel:"noopener noreferrer"},M=e("h2",{id:"invisible-man",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#invisible-man","aria-hidden":"true"},"#"),t(" Invisible Man")],-1),Q={href:"https://www.ximalaya.com/sound/414776047",target:"_blank",rel:"noopener noreferrer"},U=e("p",null,"节选自同名小说(有改编),telling a story about a black teenager present his speech before white people.",-1),X=e("h2",{id:"you-ve-got-to-find-what-you-love",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#you-ve-got-to-find-what-you-love","aria-hidden":"true"},"#"),t(" You've Got to Find What You Love")],-1),Z={href:"https://www.ximalaya.com/sound/414769322",target:"_blank",rel:"noopener noreferrer"},$=n('

Steve Jobs 在 Standford 的演讲,没想到 Jobs 声音这么年轻!演讲分享三个故事,表达主旨“follow your heart”。

精彩摘录如下: You can't connect the dots looking forward: You can only connect them looking backwards. So you have to trust
that the dots will somehow connect in your future. You have to trust in something-your destiny,life,whatever. Because
believing that the dots will connect down the road will give you the confidence to follow your heart, even when it leads
you off the well-worn path. And that would make all the difference.

Sometimes life is gonna hit you in the head with a brick. Don't lose faith. I'm convinced that the only thing that kept
me going was that I loved what I did. You've got to find what you love. And that is as true for your work as it is for
your lovers. Your work is going to fill a large part of your life, and the only way to be truly satisfied is to do what
you believe is great work. And the only way to do great work is to love what you do. If you haven't found it yet, keep
looking, and don't settle. As with all matters of the heart, you'll know when you find it. And like any great
relationship, it just gets better and better as the years roll on. So keep looking. Don't settle.

I have looked in the mirror every morning and asked myself: "If today were the last day of my life,would I want to do
what l am about to do today?" And whenever the answer has been "No" for too many days in a row, l know I need to change
something. Remembering that I'll be dead soon is the most important tool I've ever encountered to help me make the big
choices in life. Because almost everything-all external expectations, all pride, all fear of embarrassment or
failure-these things just fall away in the face of death,leaving only what is truly important. Remembering that you are
going to die is the best way I know to avoid the trap of thinking you have something to lose. You are already naked.
There is no reason not to follow your heart.

Where Do We Go from Here

',5),ee={href:"https://www.ximalaya.com/sound/414771197",target:"_blank",rel:"noopener noreferrer"},te=e("p",null,"本文是马丁·路德·金的另一篇呼吁黑人平等的演讲,从中可以看到与《I Have a Dream》的影子。",-1),oe=e("p",null,[t("摘录如下: As long as the mind is enslaved, the body can never be free. Psychological freedom,a firm sense of self-esteem, is"),e("br"),t(" the most powerful weapon against the long night of physical slavery. No Lincolnian emancipation proclamation or"),e("br"),t(" Johnsonian civil rights bill can totally bring this kind of freedom. The Negro will only be free when he reaches down to"),e("br"),t(" the inner depths of his own being and signs with the pen and ink of assertive manhood his own emancipation proclamation.")],-1),ae=e("p",null,[t("And so I say to you today that I still stand by nonviolence. For through violence you may murder a murderer but you"),e("br"),t(" can't murder murder. Through violence you may murder a liar but you can't establish truth. Through violence you may"),e("br"),t(" murder a hater,but you can't murder hate. Darkness cannot put out darkness. Only light can do that.")],-1);function ne(re,ie){const o=i("ExternalLinkIcon");return s(),h("div",null,[d,u,e("p",null,[t("全书链接:"),e("a",c,[t("https://www.ximalaya.com/album/49466046"),a(o)])]),m,p,e("p",null,[t("原文链接:"),e("a",y,[t("https://m.kekenet.com/daxue/201909/593904.shtml"),a(o)])]),f,e("p",null,[t("原文链接:"),e("a",w,[t("https://m.kekenet.com/daxue/201909/595380.shtml"),a(o)])]),b,g,_,k,v,x,e("p",null,[t("原文链接:"),e("a",I,[t("https://www.ximalaya.com/sound/414753193"),a(o)])]),T,A,e("p",null,[t("原文链接:"),e("a",Y,[t("https://www.ximalaya.com/sound/414754775"),a(o)])]),j,S,e("p",null,[t("原文链接:"),e("a",q,[t("https://www.ximalaya.com/sound/414756339"),a(o)])]),N,e("p",null,[t("原文链接:"),e("a",B,[t("https://www.ximalaya.com/sound/414757228"),a(o)])]),D,O,e("p",null,[t("原文链接:"),e("a",F,[t("https://www.ximalaya.com/sound/414759448"),a(o)])]),L,W,C,E,z,e("p",null,[t("原文链接:"),e("a",H,[t("https://m.kekenet.com/daxue/202001/603745.shtml"),a(o)])]),R,e("p",null,[t("原文链接:"),e("a",P,[t("https://www.ximalaya.com/sound/414765357"),a(o)])]),V,e("p",null,[t("原文链接:"),e("a",G,[t("https://www.ximalaya.com/sound/414767340"),a(o)])]),J,e("p",null,[t("解析:"),e("a",K,[t("https://medium.com/the-afterglow/come-rain-or-come-shine-by-kazuo-ishiguro-finding-the-moment-b09e418652db"),a(o)])]),M,e("p",null,[t("原文链接:"),e("a",Q,[t("https://www.ximalaya.com/sound/414776047"),a(o)])]),U,X,e("p",null,[t("原文链接:"),e("a",Z,[t("https://www.ximalaya.com/sound/414769322"),a(o)])]),$,e("p",null,[t("原文链接:"),e("a",ee,[t("https://www.ximalaya.com/sound/414771197"),a(o)])]),te,oe,ae])}const le=r(l,[["render",ne],["__file","contemporary-college-english-5.html.vue"]]);export{le as default}; +import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o as s,c as h,a as e,b as t,d as a,f as n}from"./app-a9d55428.js";const l={},d=e("h1",{id:"现代大学英语精读-第2版-第五册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第五册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第五册")],-1),u=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),c={href:"https://www.ximalaya.com/album/49466046",target:"_blank",rel:"noopener noreferrer"},m=e("p",null,"本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。",-1),p=e("h2",{id:"who-are-you-and-what-are-you-doing-here",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#who-are-you-and-what-are-you-doing-here","aria-hidden":"true"},"#"),t(" Who Are You and What Are You Doing Here")],-1),y={href:"https://m.kekenet.com/daxue/201909/593904.shtml",target:"_blank",rel:"noopener noreferrer"},f=n('

本文分享了作者教育观,并阐述了上大学的意义,否定了上学是为了就业的观点。

摘录如下: Education has one salient enemy in present-day America, and that enemy is education——university education in
particular. To almost every university education is a means to an end. For students, that end is a good job.

The best reason to read great writers is to see if they may know you better than you know yourself. You may find your
own suppressed and rejected thoughts flowing back to you with an "alienated majesty." Reading the great writers, you may
have the experience that Longinus associated with the sublime: You feel that you have actually created the text
yourself. For somehow your predecessors are more yourself than you are.

Trying to figure out whether the stuff you're reading is true or false and being open to having your life changed is a
fraught, controversial activity. Doing so requires energy from the professors. This kind of perspective-altering
teaching and learning can cause the things which administrators fear above all else: trouble, arguments, bad press, etc.

Two Kinds

',5),w={href:"https://m.kekenet.com/daxue/201909/595380.shtml",target:"_blank",rel:"noopener noreferrer"},b=e("p",null,"本文以女儿的视角讲述了母亲望女成凤而女儿叛逆的故事,文中母亲代表的中国式家庭教育观,我想不少人会感同身受。用现在的眼光看来,这只不过是一段充满了失败的沟通、无法双赢的家庭教育经历,借助非暴力沟通或双赢思维是能够避免的。",-1),g=e("p",null,[t("摘录如下: I didn't budge. And then I decided. I didn't have to do what my mother said anymore. I wasn't her slave. This"),e("br"),t(" wasn't China. I had listened to her before and look what happened. She was the stupid one.")],-1),_=e("p",null,`You want me to be someone that I'm not!" I sobbed. "I will never be the kind of daughter you want me to be!"`,-1),k=e("p",null,[t('"Only two kinds of daughters," she shouted in Chinese. "Those who are obedient and those who follow their own mind! Only'),e("br"),t(' one kind of daughter can live in this house. Obedient daughter!"')],-1),v=e("p",null,`"Then I wish I wasn't your daughter, I wish you weren't my mother," I shouted.`,-1),x=e("h2",{id:"love-is-a-fallacy",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#love-is-a-fallacy","aria-hidden":"true"},"#"),t(" Love is a Fallacy")],-1),I={href:"https://www.ximalaya.com/sound/414753193",target:"_blank",rel:"noopener noreferrer"},T=e("p",null,"这多篇看下来,这篇是目前最有意思、最有趣、最令人忍俊不禁的。为避免剧透,我就少说一点。本文首尾响应,完美闭环,看完后我直呼我直呼,哈哈哈哈。就说到这了,总之墙裂推荐。",-1),A=e("h2",{id:"rewriting-american-history",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#rewriting-american-history","aria-hidden":"true"},"#"),t(" Rewriting American History")],-1),Y={href:"https://www.ximalaya.com/sound/414754775",target:"_blank",rel:"noopener noreferrer"},j=e("p",null,"本文讲述的是美国历史教材的一些变化及背后的思考。我认可其中的一个做法:不是对历史给一个所谓正统的结论,而是充分准备材料,从不同的角度陈述观点,引导人思考。",-1),S=e("h2",{id:"nobel-peace-price-about-global-warming",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#nobel-peace-price-about-global-warming","aria-hidden":"true"},"#"),t(" Nobel Peace Price About Global Warming")],-1),q={href:"https://www.ximalaya.com/sound/414756339",target:"_blank",rel:"noopener noreferrer"},N=e("h2",{id:"the-bluest-eyes",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-bluest-eyes","aria-hidden":"true"},"#"),t(" The Bluest Eyes")],-1),B={href:"https://www.ximalaya.com/sound/414757228",target:"_blank",rel:"noopener noreferrer"},D=e("p",null,"这是个记叙文,然而,我认为确是这么多篇文章中,最难懂的。",-1),O=e("h2",{id:"how-news-becomes-options-and-opinions-off-limits",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#how-news-becomes-options-and-opinions-off-limits","aria-hidden":"true"},"#"),t(" How News Becomes Options and Opinions Off-Limits")],-1),F={href:"https://www.ximalaya.com/sound/414759448",target:"_blank",rel:"noopener noreferrer"},L=e("p",null,"本文说明了当代一种社会现代:人们在看新闻,不是在看“事实”,而是在看“观点”,作者认为,现在的新闻跟小说并非没有相似之处了。然而,作者对这种现象并非完全持否定态度,他认为这是言论自由的一种方式。最后,作者还表达了在自由社会对“尊重”一词的看法。",-1),W=e("p",null,[t("摘录如下: In any version of a free society, the value of free speech must rank the highest, for that is the freedom without"),e("br"),t(" which all other freedoms would fall.")],-1),C=e("p",null,[t("In free societies, you must have the free play of ideas. There must be arguments, and it must be impassioned and"),e("br"),t(" untrammeled.")],-1),E=e("p",null,[t("A free society is not a calm and eventless place——that is the kind of static, dead society dictators try to create. Free"),e("br"),t(" societies are dynamic, noisy, turbulent and full of radical disagreements.")],-1),z=e("h2",{id:"the-indispensable-opposition",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-indispensable-opposition","aria-hidden":"true"},"#"),t(" The Indispensable Opposition")],-1),H={href:"https://m.kekenet.com/daxue/202001/603745.shtml",target:"_blank",rel:"noopener noreferrer"},R=n('

本文所表达的观点,属于民主与自由思想的经典内容:不可缺少的反对派(或忠诚的反对派)。

摘录如下: I wholly disapprove of what you say, but will defend to the death your right to say it.

If we truly wish to understand why freedom is necessary in a civilized society, we must begin by realizing that, because
freedom of discussion improves our own opinions, the liberties of other men are our own vital necessity.

We must insist that free oratory is only the beginning of free speech; it is not the end, but a means to an end. The end
is to find the truth.

The only reason for dwelling on all this is that if we are to preserve democracy we must understand its principles. And
the principle which distinguishes it from all other forms of government is that in a democracy the opposition not only
is tolerated as constitutional but must be maintained because it is in fact indispensable.

The democratic system cannot be operated without effective opposition. For, in making the great experiment of governing
people by consent rather than by coercion, it is not sufficient that the party in power should have a majority. It is
just as necessary that the party in power should never outrage the minority. That means that it must listen to the
minority and be moved by the criticisms of the minority. That means that its measures must take account of the
minority's objections, and that in administering measures it must remember that the minority may become the majority.

The Danger of a Single Story

',7),P={href:"https://www.ximalaya.com/sound/414765357",target:"_blank",rel:"noopener noreferrer"},V=n('

认识到硬币有正反面,并坚持凡事至少从两个角度去看待与分析,那么本篇文章就没白读。

摘录如下: It is impossible to talk about the single story without talking about power. Power is the ability not just to tell
the story of another person, but to make it the definitive story of that person.

All of these stories make me who l am. But to insist on only these negative stories is to flatten my experience and to
overlook the many other stories that formed me.

The single story creates stereotypes,and the problem with stereotypes is not that theyare untrue, but that they are
incomplete. They make one story become the only story.

The consequence of the single story is this: It robs people of dignity. It makes our recognition of our equal humanity
difficult. It emphasizes how we are different rather than how we are similar.

Come Rain or Come Shine

',6),G={href:"https://www.ximalaya.com/sound/414767340",target:"_blank",rel:"noopener noreferrer"},J=e("p",null,"看得不太懂,但故事倒挺有意思的。",-1),K={href:"https://medium.com/the-afterglow/come-rain-or-come-shine-by-kazuo-ishiguro-finding-the-moment-b09e418652db",target:"_blank",rel:"noopener noreferrer"},M=e("h2",{id:"invisible-man",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#invisible-man","aria-hidden":"true"},"#"),t(" Invisible Man")],-1),Q={href:"https://www.ximalaya.com/sound/414776047",target:"_blank",rel:"noopener noreferrer"},U=e("p",null,"节选自同名小说(有改编),telling a story about a black teenager present his speech before white people.",-1),X=e("h2",{id:"you-ve-got-to-find-what-you-love",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#you-ve-got-to-find-what-you-love","aria-hidden":"true"},"#"),t(" You've Got to Find What You Love")],-1),Z={href:"https://www.ximalaya.com/sound/414769322",target:"_blank",rel:"noopener noreferrer"},$=n('

Steve Jobs 在 Standford 的演讲,没想到 Jobs 声音这么年轻!演讲分享三个故事,表达主旨“follow your heart”。

精彩摘录如下: You can't connect the dots looking forward: You can only connect them looking backwards. So you have to trust
that the dots will somehow connect in your future. You have to trust in something-your destiny,life,whatever. Because
believing that the dots will connect down the road will give you the confidence to follow your heart, even when it leads
you off the well-worn path. And that would make all the difference.

Sometimes life is gonna hit you in the head with a brick. Don't lose faith. I'm convinced that the only thing that kept
me going was that I loved what I did. You've got to find what you love. And that is as true for your work as it is for
your lovers. Your work is going to fill a large part of your life, and the only way to be truly satisfied is to do what
you believe is great work. And the only way to do great work is to love what you do. If you haven't found it yet, keep
looking, and don't settle. As with all matters of the heart, you'll know when you find it. And like any great
relationship, it just gets better and better as the years roll on. So keep looking. Don't settle.

I have looked in the mirror every morning and asked myself: "If today were the last day of my life,would I want to do
what l am about to do today?" And whenever the answer has been "No" for too many days in a row, l know I need to change
something. Remembering that I'll be dead soon is the most important tool I've ever encountered to help me make the big
choices in life. Because almost everything-all external expectations, all pride, all fear of embarrassment or
failure-these things just fall away in the face of death,leaving only what is truly important. Remembering that you are
going to die is the best way I know to avoid the trap of thinking you have something to lose. You are already naked.
There is no reason not to follow your heart.

Where Do We Go from Here

',5),ee={href:"https://www.ximalaya.com/sound/414771197",target:"_blank",rel:"noopener noreferrer"},te=e("p",null,"本文是马丁·路德·金的另一篇呼吁黑人平等的演讲,从中可以看到与《I Have a Dream》的影子。",-1),oe=e("p",null,[t("摘录如下: As long as the mind is enslaved, the body can never be free. Psychological freedom,a firm sense of self-esteem, is"),e("br"),t(" the most powerful weapon against the long night of physical slavery. No Lincolnian emancipation proclamation or"),e("br"),t(" Johnsonian civil rights bill can totally bring this kind of freedom. The Negro will only be free when he reaches down to"),e("br"),t(" the inner depths of his own being and signs with the pen and ink of assertive manhood his own emancipation proclamation.")],-1),ae=e("p",null,[t("And so I say to you today that I still stand by nonviolence. For through violence you may murder a murderer but you"),e("br"),t(" can't murder murder. Through violence you may murder a liar but you can't establish truth. Through violence you may"),e("br"),t(" murder a hater,but you can't murder hate. Darkness cannot put out darkness. Only light can do that.")],-1);function ne(re,ie){const o=i("ExternalLinkIcon");return s(),h("div",null,[d,u,e("p",null,[t("全书链接:"),e("a",c,[t("https://www.ximalaya.com/album/49466046"),a(o)])]),m,p,e("p",null,[t("原文链接:"),e("a",y,[t("https://m.kekenet.com/daxue/201909/593904.shtml"),a(o)])]),f,e("p",null,[t("原文链接:"),e("a",w,[t("https://m.kekenet.com/daxue/201909/595380.shtml"),a(o)])]),b,g,_,k,v,x,e("p",null,[t("原文链接:"),e("a",I,[t("https://www.ximalaya.com/sound/414753193"),a(o)])]),T,A,e("p",null,[t("原文链接:"),e("a",Y,[t("https://www.ximalaya.com/sound/414754775"),a(o)])]),j,S,e("p",null,[t("原文链接:"),e("a",q,[t("https://www.ximalaya.com/sound/414756339"),a(o)])]),N,e("p",null,[t("原文链接:"),e("a",B,[t("https://www.ximalaya.com/sound/414757228"),a(o)])]),D,O,e("p",null,[t("原文链接:"),e("a",F,[t("https://www.ximalaya.com/sound/414759448"),a(o)])]),L,W,C,E,z,e("p",null,[t("原文链接:"),e("a",H,[t("https://m.kekenet.com/daxue/202001/603745.shtml"),a(o)])]),R,e("p",null,[t("原文链接:"),e("a",P,[t("https://www.ximalaya.com/sound/414765357"),a(o)])]),V,e("p",null,[t("原文链接:"),e("a",G,[t("https://www.ximalaya.com/sound/414767340"),a(o)])]),J,e("p",null,[t("解析:"),e("a",K,[t("https://medium.com/the-afterglow/come-rain-or-come-shine-by-kazuo-ishiguro-finding-the-moment-b09e418652db"),a(o)])]),M,e("p",null,[t("原文链接:"),e("a",Q,[t("https://www.ximalaya.com/sound/414776047"),a(o)])]),U,X,e("p",null,[t("原文链接:"),e("a",Z,[t("https://www.ximalaya.com/sound/414769322"),a(o)])]),$,e("p",null,[t("原文链接:"),e("a",ee,[t("https://www.ximalaya.com/sound/414771197"),a(o)])]),te,oe,ae])}const le=r(l,[["render",ne],["__file","contemporary-college-english-5.html.vue"]]);export{le as default}; diff --git a/assets/contemporary-college-english-5.html-461e5519.js b/assets/contemporary-college-english-5.html-c752988e.js similarity index 94% rename from assets/contemporary-college-english-5.html-461e5519.js rename to assets/contemporary-college-english-5.html-c752988e.js index bd381fa8..b52b4660 100644 --- a/assets/contemporary-college-english-5.html-461e5519.js +++ b/assets/contemporary-college-english-5.html-c752988e.js @@ -1 +1 @@ -const e=JSON.parse(`{"key":"v-89760306","path":"/english/contemporary-college-english-5.html","title":"现代大学英语精读(第2版)第五册","lang":"zh-CN","frontmatter":{"date":"2022-08-28T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第五册 介绍 全书链接:https://www.ximalaya.com/album/49466046 本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。 Who Are You and What Are You Doing Here","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-5.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第五册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第五册 介绍 全书链接:https://www.ximalaya.com/album/49466046 本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。 Who Are You and What Are You Doing Here"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-08-28T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第五册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-08-28T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"Who Are You and What Are You Doing Here","slug":"who-are-you-and-what-are-you-doing-here","link":"#who-are-you-and-what-are-you-doing-here","children":[]},{"level":2,"title":"Two Kinds","slug":"two-kinds","link":"#two-kinds","children":[]},{"level":2,"title":"Love is a Fallacy","slug":"love-is-a-fallacy","link":"#love-is-a-fallacy","children":[]},{"level":2,"title":"Rewriting American History","slug":"rewriting-american-history","link":"#rewriting-american-history","children":[]},{"level":2,"title":"Nobel Peace Price About Global Warming","slug":"nobel-peace-price-about-global-warming","link":"#nobel-peace-price-about-global-warming","children":[]},{"level":2,"title":"The Bluest Eyes","slug":"the-bluest-eyes","link":"#the-bluest-eyes","children":[]},{"level":2,"title":"How News Becomes Options and Opinions Off-Limits","slug":"how-news-becomes-options-and-opinions-off-limits","link":"#how-news-becomes-options-and-opinions-off-limits","children":[]},{"level":2,"title":"The Indispensable Opposition","slug":"the-indispensable-opposition","link":"#the-indispensable-opposition","children":[]},{"level":2,"title":"The Danger of a Single Story","slug":"the-danger-of-a-single-story","link":"#the-danger-of-a-single-story","children":[]},{"level":2,"title":"Come Rain or Come Shine","slug":"come-rain-or-come-shine","link":"#come-rain-or-come-shine","children":[]},{"level":2,"title":"Invisible Man","slug":"invisible-man","link":"#invisible-man","children":[]},{"level":2,"title":"You've Got to Find What You Love","slug":"you-ve-got-to-find-what-you-love","link":"#you-ve-got-to-find-what-you-love","children":[]},{"level":2,"title":"Where Do We Go from Here","slug":"where-do-we-go-from-here","link":"#where-do-we-go-from-here","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":7.18,"words":2155},"filePathRelative":"english/contemporary-college-english-5.md","localizedDate":"2022年8月28日","excerpt":"

现代大学英语精读(第2版)第五册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/49466046

\\n

本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。

\\n

Who Are You and What Are You Doing Here

","autoDesc":true}`);export{e as data}; +const e=JSON.parse(`{"key":"v-89760306","path":"/english/contemporary-college-english-5.html","title":"现代大学英语精读(第2版)第五册","lang":"zh-CN","frontmatter":{"date":"2022-08-28T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第五册 介绍 全书链接:https://www.ximalaya.com/album/49466046 本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。 Who Are You and What Are You Doing Here","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-5.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第五册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第五册 介绍 全书链接:https://www.ximalaya.com/album/49466046 本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。 Who Are You and What Are You Doing Here"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-08-28T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第五册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-08-28T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"Who Are You and What Are You Doing Here","slug":"who-are-you-and-what-are-you-doing-here","link":"#who-are-you-and-what-are-you-doing-here","children":[]},{"level":2,"title":"Two Kinds","slug":"two-kinds","link":"#two-kinds","children":[]},{"level":2,"title":"Love is a Fallacy","slug":"love-is-a-fallacy","link":"#love-is-a-fallacy","children":[]},{"level":2,"title":"Rewriting American History","slug":"rewriting-american-history","link":"#rewriting-american-history","children":[]},{"level":2,"title":"Nobel Peace Price About Global Warming","slug":"nobel-peace-price-about-global-warming","link":"#nobel-peace-price-about-global-warming","children":[]},{"level":2,"title":"The Bluest Eyes","slug":"the-bluest-eyes","link":"#the-bluest-eyes","children":[]},{"level":2,"title":"How News Becomes Options and Opinions Off-Limits","slug":"how-news-becomes-options-and-opinions-off-limits","link":"#how-news-becomes-options-and-opinions-off-limits","children":[]},{"level":2,"title":"The Indispensable Opposition","slug":"the-indispensable-opposition","link":"#the-indispensable-opposition","children":[]},{"level":2,"title":"The Danger of a Single Story","slug":"the-danger-of-a-single-story","link":"#the-danger-of-a-single-story","children":[]},{"level":2,"title":"Come Rain or Come Shine","slug":"come-rain-or-come-shine","link":"#come-rain-or-come-shine","children":[]},{"level":2,"title":"Invisible Man","slug":"invisible-man","link":"#invisible-man","children":[]},{"level":2,"title":"You've Got to Find What You Love","slug":"you-ve-got-to-find-what-you-love","link":"#you-ve-got-to-find-what-you-love","children":[]},{"level":2,"title":"Where Do We Go from Here","slug":"where-do-we-go-from-here","link":"#where-do-we-go-from-here","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":7.18,"words":2155},"filePathRelative":"english/contemporary-college-english-5.md","localizedDate":"2022年8月28日","excerpt":"

现代大学英语精读(第2版)第五册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/49466046

\\n

本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。

\\n

Who Are You and What Are You Doing Here

","autoDesc":true}`);export{e as data}; diff --git a/assets/contemporary-college-english-6.html-cadc7c30.js b/assets/contemporary-college-english-6.html-3f6139cd.js similarity index 98% rename from assets/contemporary-college-english-6.html-cadc7c30.js rename to assets/contemporary-college-english-6.html-3f6139cd.js index ceb5c215..2746bb02 100644 --- a/assets/contemporary-college-english-6.html-cadc7c30.js +++ b/assets/contemporary-college-english-6.html-3f6139cd.js @@ -1 +1 @@ -import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as s,c as h,a as e,b as t,d as a,f as n}from"./app-b649ee34.js";const l={},d=e("h1",{id:"现代大学英语精读-第2版-第六册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第六册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第六册")],-1),c=e("h2",{id:"前言",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),t(" 前言")],-1),u={href:"https://www.ximalaya.com/album/49468954",target:"_blank",rel:"noopener noreferrer"},p=e("p",null,"本册是整个系列的最后一册了,完结撒花🎉",-1),f=e("h2",{id:"paper-tigers",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#paper-tigers","aria-hidden":"true"},"#"),t(" Paper Tigers")],-1),m={href:"https://www.ximalaya.com/sound/414998175",target:"_blank",rel:"noopener noreferrer"},w=n("

探讨了 Asian American 的教育经历与社会成就不符的现象。

摘录如下: Let me summarize my feelings toward Asian values: Damn filial piety. Damn grade grubbing. Damn Ivy League mania.
Damn deference to authority. Damn humility and hard work. Damn harmonious relations. Damn sacrificing for the future.
Damn earnest, striving middle-class servility.

Maybe a traditionally Asian upbringing is the problem. In order to be a leader, you must have followers. Associates are
initially judged on how well they do the work they are assigned. But being a leader requires different skill sets. “The
traits that got you to where you are won't necessarily take you to the next level," says the diversity consultant Jane
Hyun, who wrote a book called Breaking the Bamboo Ceiling.

At Yale, Chua made the connection between her upbringing and her adult dissatisfaction. “My parents didn't sit around
talking about politics and philosophy at the dinner table," she told the students. Even after she had escaped from
corporate law and made it onto a law faculty,“I was kind of lost. I just didn't feel the passion."

Chua's Chinese education had gotten her through an elite schooling, but it left her unprepared for the real world.

",5),b=e("br",null,null,-1),y={href:"https://baike.baidu.com/item/%E8%94%A1%E7%BE%8E%E5%84%BF/3424632#7",target:"_blank",rel:"noopener noreferrer"},g=e("h2",{id:"what-is-news",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#what-is-news","aria-hidden":"true"},"#"),t(" What Is News")],-1),_={href:"https://www.ximalaya.com/sound/414999846",target:"_blank",rel:"noopener noreferrer"},x=n('

提示我们如何看待新闻,与前面的如何看待历史可谓是姐妹篇。

摘录如下: The news is made rather than gather.

We see what we expect to see. We focus on what we are paid to see. And those who pay us to see usually expect us to
accept their notions.

“What is news?" News,we might say, may be history in its first and best form, or the stuff of literature, or a record of
the condition of a society, or the expression of things, but in its worst form it can also be mainly a “filler,” a
“come-on" to keep the viewer's attention until the commercials come.

All of which leads us to reiterate, first, that there are no simple answers to the question “What is news?"
And, second, that it is not our purpose to tell you what you ought to believe about the question. The purpose of this
chapter is to arouse your interest in thinking about the question. Your answers are to be found by knowing what you feel
is significant and how your sense of the significant conforms with or departs from that of others. Answers are to be
found in your ideas about the purpose of public communication, and in your judgment of the kind of society you live in
and wish to live in. We cannot provide answers to these questions. But you also need to know something about the
problems, limitations, traditions, motivations, and, yes, even the delusions of the television news industry.

At War with the Planet

',6),k={href:"https://www.ximalaya.com/sound/415001340",target:"_blank",rel:"noopener noreferrer"},v=e("p",null,"本文为了我们科普了人类社会与自然环境的特征,提供了一个新颖而深刻的视角来看待人与自然的关系,启发我们在采取人与自然和谐相处的措施时,不要走极端。",-1),T=e("h2",{id:"how-to-get-the-poor-off-our-conscience",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#how-to-get-the-poor-off-our-conscience","aria-hidden":"true"},"#"),t(" How to Get the Poor off Our Conscience")],-1),A={href:"https://www.ximalaya.com/sound/415004615",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,"本文讨论了面对一直存在的贫富差距现象,社会思想、政府措施在历史上经历了怎样的变化。",-1),C=e("h2",{id:"housewifely-arts",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#housewifely-arts","aria-hidden":"true"},"#"),t(" Housewifely Arts")],-1),I={href:"https://www.ximalaya.com/sound/415006607",target:"_blank",rel:"noopener noreferrer"},q=e("p",null,"本文主要表达的是对母亲的回忆,其实属于可看可不看类型,之所以还放上来,原因有二:",-1),B=e("ol",null,[e("li",null,"冷幽默,毕竟有几处地方让我笑了"),e("li",null,"引以为戒,千万别学女主")],-1),D=e("h2",{id:"the-one-against-the-many",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-one-against-the-many","aria-hidden":"true"},"#"),t(" The One Against The Many")],-1),N={href:"https://www.ximalaya.com/sound/415007539",target:"_blank",rel:"noopener noreferrer"},W=n('

本文主要是讲美国文明的,没有一个地方出现China,但几乎处处都能看到China的影子。绝对不容错过的好文,可以说是反洗脑的基础。

精彩摘录: It is important here to insist on the distinction between ideals and ideology. Ideals refer to the long-run goals
of a nation and the spirit in which these goals are pursued. Ideology is something different, more systematic, more
detailed, more comprehensive, more dogmatic.

An ideology, in other words, is an abstraction from reality. There is nothing wrong with abstractions or models. In
fact, we could not conduct discourse without them. The ideological fallacy is to forget that ideology is an abstraction
from reality and to regard it as reality itself. The besetting sin of the ideologist, in short, is to confuse his own
tidy models with the vast, turbulent, unpredictable, and untidy reality which is the stuff of human experience.

Consider for a moment the ideologist's view of history. The ideologist contends that the mysteries of history can be
understood in terms of a clear-cut, absolute, social creed which explains the past and forecasts the future. Ideology
thus presupposes a closed universe whose history is determined, whose principles are fixed, whose values and objectives
are deducible from a central body of social dogma and often whose central dogma is confided to the custody of an
infallible priesthood.

The American tradition has found this view of human history repugnant and false, against the belief in the
all-encompassing power of a single explanation, against the commitment to the absolutism of ideology, against the notion
that all answers to political and social problems can be found in the back of some sacred book, against the
deterministic interpretation of history.

Ideologists are afraid of the free flow of ideas, even of deviant ideas within their own ideology. They are convinced
they have a monopoly on the Truth. Therefore they always feel that they are only saving the world when they slaughter
the heretics. Their objective remains that of making the world over in the image of their dogmatic ideology. The goal is
a monolithic world, organized on the principle of infallibility-but the only certainty in an absolute system is the
certainty of absolute abuse.

The goal of free men is quite different. Free men know many truths, but they doubt whether any mortal man knows the
Truth. Their religious and their intellectual heritage join in leading them to suspect fellow men who lay claim to
infallibility. They believe that there is no greater delusion than for man to mistake himself for God. They accept the
limitations of the human intellect and the infirmity of the human spirit.

Notes on the English Character

',8),j={href:"https://www.ximalaya.com/sound/415017440",target:"_blank",rel:"noopener noreferrer"},P=e("p",null,"本文对英格兰人的性格特征进行了简单的探讨。",-1),F=e("h2",{id:"the-death-of-a-pig",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-death-of-a-pig","aria-hidden":"true"},"#"),t(" The Death of a Pig")],-1),H={href:"https://www.ximalaya.com/sound/415019607",target:"_blank",rel:"noopener noreferrer"},L=e("p",null,"本文主人公本计划养一只猪来吃,却养死了。为何把这篇文章收录进来呢,可能是因为里面细节写得好吧,把主人公的病急乱投医的场景写得较为生动。",-1),V=e("h2",{id:"don-t-eat-fortune-s-cookie",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#don-t-eat-fortune-s-cookie","aria-hidden":"true"},"#"),t(" Don't Eat Fortune's Cookie")],-1),M={href:"https://www.ximalaya.com/sound/415020743",target:"_blank",rel:"noopener noreferrer"},z=e("p",null,"Princeton学长回校演讲,核心观点是:你以为你的成功全靠实力,然而其中隐含着运气。",-1),G=e("h2",{id:"the-accidental-universe",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-accidental-universe","aria-hidden":"true"},"#"),t(" The Accidental Universe")],-1),O={href:"https://www.ximalaya.com/sound/415022329",target:"_blank",rel:"noopener noreferrer"},S=e("p",null,"本文是物理相关的说明文,讲了多重宇宙假设,提出一种观点:我们所在的宇宙是出于偶然。",-1),Y=e("h2",{id:"rowling-s-speech-at-harvard",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#rowling-s-speech-at-harvard","aria-hidden":"true"},"#"),t(" Rowling's Speech at Harvard")],-1),J={href:"https://www.ximalaya.com/sound/415018537",target:"_blank",rel:"noopener noreferrer"},R=e("p",null,"罗琳的英音很好听,而且本次演讲也很幽默,建议边看边听。",-1);function U(K,Q){const o=r("ExternalLinkIcon");return s(),h("div",null,[d,c,e("p",null,[t("全书链接:"),e("a",u,[t("https://www.ximalaya.com/album/49468954"),a(o)])]),p,f,e("p",null,[t("原文链接:"),e("a",m,[t("https://www.ximalaya.com/sound/414998175"),a(o)])]),w,e("p",null,[t("注:Chua"),b,t(" 也就是《虎妈战歌》的作者:"),e("a",y,[t("https://baike.baidu.com/item/%E8%94%A1%E7%BE%8E%E5%84%BF/3424632#7"),a(o)])]),g,e("p",null,[t("原文链接:"),e("a",_,[t("https://www.ximalaya.com/sound/414999846"),a(o)])]),x,e("p",null,[t("原文链接:"),e("a",k,[t("https://www.ximalaya.com/sound/415001340"),a(o)])]),v,T,e("p",null,[t("原文链接:"),e("a",A,[t("https://www.ximalaya.com/sound/415004615"),a(o)])]),E,C,e("p",null,[t("原文链接:"),e("a",I,[t("https://www.ximalaya.com/sound/415006607"),a(o)])]),q,B,D,e("p",null,[t("原文链接:"),e("a",N,[t("https://www.ximalaya.com/sound/415007539"),a(o)])]),W,e("p",null,[t("原文链接:"),e("a",j,[t("https://www.ximalaya.com/sound/415017440"),a(o)])]),P,F,e("p",null,[t("原文链接:"),e("a",H,[t("https://www.ximalaya.com/sound/415019607"),a(o)])]),L,V,e("p",null,[t("原文链接:"),e("a",M,[t("https://www.ximalaya.com/sound/415020743"),a(o)])]),z,G,e("p",null,[t("原文链接:"),e("a",O,[t("https://www.ximalaya.com/sound/415022329"),a(o)])]),S,Y,e("p",null,[t("原文链接:"),e("a",J,[t("https://www.ximalaya.com/sound/415018537"),a(o)])]),R])}const $=i(l,[["render",U],["__file","contemporary-college-english-6.html.vue"]]);export{$ as default}; +import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as s,c as h,a as e,b as t,d as a,f as n}from"./app-a9d55428.js";const l={},d=e("h1",{id:"现代大学英语精读-第2版-第六册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第六册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第六册")],-1),c=e("h2",{id:"前言",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),t(" 前言")],-1),u={href:"https://www.ximalaya.com/album/49468954",target:"_blank",rel:"noopener noreferrer"},p=e("p",null,"本册是整个系列的最后一册了,完结撒花🎉",-1),f=e("h2",{id:"paper-tigers",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#paper-tigers","aria-hidden":"true"},"#"),t(" Paper Tigers")],-1),m={href:"https://www.ximalaya.com/sound/414998175",target:"_blank",rel:"noopener noreferrer"},w=n("

探讨了 Asian American 的教育经历与社会成就不符的现象。

摘录如下: Let me summarize my feelings toward Asian values: Damn filial piety. Damn grade grubbing. Damn Ivy League mania.
Damn deference to authority. Damn humility and hard work. Damn harmonious relations. Damn sacrificing for the future.
Damn earnest, striving middle-class servility.

Maybe a traditionally Asian upbringing is the problem. In order to be a leader, you must have followers. Associates are
initially judged on how well they do the work they are assigned. But being a leader requires different skill sets. “The
traits that got you to where you are won't necessarily take you to the next level," says the diversity consultant Jane
Hyun, who wrote a book called Breaking the Bamboo Ceiling.

At Yale, Chua made the connection between her upbringing and her adult dissatisfaction. “My parents didn't sit around
talking about politics and philosophy at the dinner table," she told the students. Even after she had escaped from
corporate law and made it onto a law faculty,“I was kind of lost. I just didn't feel the passion."

Chua's Chinese education had gotten her through an elite schooling, but it left her unprepared for the real world.

",5),b=e("br",null,null,-1),y={href:"https://baike.baidu.com/item/%E8%94%A1%E7%BE%8E%E5%84%BF/3424632#7",target:"_blank",rel:"noopener noreferrer"},g=e("h2",{id:"what-is-news",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#what-is-news","aria-hidden":"true"},"#"),t(" What Is News")],-1),_={href:"https://www.ximalaya.com/sound/414999846",target:"_blank",rel:"noopener noreferrer"},x=n('

提示我们如何看待新闻,与前面的如何看待历史可谓是姐妹篇。

摘录如下: The news is made rather than gather.

We see what we expect to see. We focus on what we are paid to see. And those who pay us to see usually expect us to
accept their notions.

“What is news?" News,we might say, may be history in its first and best form, or the stuff of literature, or a record of
the condition of a society, or the expression of things, but in its worst form it can also be mainly a “filler,” a
“come-on" to keep the viewer's attention until the commercials come.

All of which leads us to reiterate, first, that there are no simple answers to the question “What is news?"
And, second, that it is not our purpose to tell you what you ought to believe about the question. The purpose of this
chapter is to arouse your interest in thinking about the question. Your answers are to be found by knowing what you feel
is significant and how your sense of the significant conforms with or departs from that of others. Answers are to be
found in your ideas about the purpose of public communication, and in your judgment of the kind of society you live in
and wish to live in. We cannot provide answers to these questions. But you also need to know something about the
problems, limitations, traditions, motivations, and, yes, even the delusions of the television news industry.

At War with the Planet

',6),k={href:"https://www.ximalaya.com/sound/415001340",target:"_blank",rel:"noopener noreferrer"},v=e("p",null,"本文为了我们科普了人类社会与自然环境的特征,提供了一个新颖而深刻的视角来看待人与自然的关系,启发我们在采取人与自然和谐相处的措施时,不要走极端。",-1),T=e("h2",{id:"how-to-get-the-poor-off-our-conscience",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#how-to-get-the-poor-off-our-conscience","aria-hidden":"true"},"#"),t(" How to Get the Poor off Our Conscience")],-1),A={href:"https://www.ximalaya.com/sound/415004615",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,"本文讨论了面对一直存在的贫富差距现象,社会思想、政府措施在历史上经历了怎样的变化。",-1),C=e("h2",{id:"housewifely-arts",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#housewifely-arts","aria-hidden":"true"},"#"),t(" Housewifely Arts")],-1),I={href:"https://www.ximalaya.com/sound/415006607",target:"_blank",rel:"noopener noreferrer"},q=e("p",null,"本文主要表达的是对母亲的回忆,其实属于可看可不看类型,之所以还放上来,原因有二:",-1),B=e("ol",null,[e("li",null,"冷幽默,毕竟有几处地方让我笑了"),e("li",null,"引以为戒,千万别学女主")],-1),D=e("h2",{id:"the-one-against-the-many",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-one-against-the-many","aria-hidden":"true"},"#"),t(" The One Against The Many")],-1),N={href:"https://www.ximalaya.com/sound/415007539",target:"_blank",rel:"noopener noreferrer"},W=n('

本文主要是讲美国文明的,没有一个地方出现China,但几乎处处都能看到China的影子。绝对不容错过的好文,可以说是反洗脑的基础。

精彩摘录: It is important here to insist on the distinction between ideals and ideology. Ideals refer to the long-run goals
of a nation and the spirit in which these goals are pursued. Ideology is something different, more systematic, more
detailed, more comprehensive, more dogmatic.

An ideology, in other words, is an abstraction from reality. There is nothing wrong with abstractions or models. In
fact, we could not conduct discourse without them. The ideological fallacy is to forget that ideology is an abstraction
from reality and to regard it as reality itself. The besetting sin of the ideologist, in short, is to confuse his own
tidy models with the vast, turbulent, unpredictable, and untidy reality which is the stuff of human experience.

Consider for a moment the ideologist's view of history. The ideologist contends that the mysteries of history can be
understood in terms of a clear-cut, absolute, social creed which explains the past and forecasts the future. Ideology
thus presupposes a closed universe whose history is determined, whose principles are fixed, whose values and objectives
are deducible from a central body of social dogma and often whose central dogma is confided to the custody of an
infallible priesthood.

The American tradition has found this view of human history repugnant and false, against the belief in the
all-encompassing power of a single explanation, against the commitment to the absolutism of ideology, against the notion
that all answers to political and social problems can be found in the back of some sacred book, against the
deterministic interpretation of history.

Ideologists are afraid of the free flow of ideas, even of deviant ideas within their own ideology. They are convinced
they have a monopoly on the Truth. Therefore they always feel that they are only saving the world when they slaughter
the heretics. Their objective remains that of making the world over in the image of their dogmatic ideology. The goal is
a monolithic world, organized on the principle of infallibility-but the only certainty in an absolute system is the
certainty of absolute abuse.

The goal of free men is quite different. Free men know many truths, but they doubt whether any mortal man knows the
Truth. Their religious and their intellectual heritage join in leading them to suspect fellow men who lay claim to
infallibility. They believe that there is no greater delusion than for man to mistake himself for God. They accept the
limitations of the human intellect and the infirmity of the human spirit.

Notes on the English Character

',8),j={href:"https://www.ximalaya.com/sound/415017440",target:"_blank",rel:"noopener noreferrer"},P=e("p",null,"本文对英格兰人的性格特征进行了简单的探讨。",-1),F=e("h2",{id:"the-death-of-a-pig",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-death-of-a-pig","aria-hidden":"true"},"#"),t(" The Death of a Pig")],-1),H={href:"https://www.ximalaya.com/sound/415019607",target:"_blank",rel:"noopener noreferrer"},L=e("p",null,"本文主人公本计划养一只猪来吃,却养死了。为何把这篇文章收录进来呢,可能是因为里面细节写得好吧,把主人公的病急乱投医的场景写得较为生动。",-1),V=e("h2",{id:"don-t-eat-fortune-s-cookie",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#don-t-eat-fortune-s-cookie","aria-hidden":"true"},"#"),t(" Don't Eat Fortune's Cookie")],-1),M={href:"https://www.ximalaya.com/sound/415020743",target:"_blank",rel:"noopener noreferrer"},z=e("p",null,"Princeton学长回校演讲,核心观点是:你以为你的成功全靠实力,然而其中隐含着运气。",-1),G=e("h2",{id:"the-accidental-universe",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-accidental-universe","aria-hidden":"true"},"#"),t(" The Accidental Universe")],-1),O={href:"https://www.ximalaya.com/sound/415022329",target:"_blank",rel:"noopener noreferrer"},S=e("p",null,"本文是物理相关的说明文,讲了多重宇宙假设,提出一种观点:我们所在的宇宙是出于偶然。",-1),Y=e("h2",{id:"rowling-s-speech-at-harvard",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#rowling-s-speech-at-harvard","aria-hidden":"true"},"#"),t(" Rowling's Speech at Harvard")],-1),J={href:"https://www.ximalaya.com/sound/415018537",target:"_blank",rel:"noopener noreferrer"},R=e("p",null,"罗琳的英音很好听,而且本次演讲也很幽默,建议边看边听。",-1);function U(K,Q){const o=r("ExternalLinkIcon");return s(),h("div",null,[d,c,e("p",null,[t("全书链接:"),e("a",u,[t("https://www.ximalaya.com/album/49468954"),a(o)])]),p,f,e("p",null,[t("原文链接:"),e("a",m,[t("https://www.ximalaya.com/sound/414998175"),a(o)])]),w,e("p",null,[t("注:Chua"),b,t(" 也就是《虎妈战歌》的作者:"),e("a",y,[t("https://baike.baidu.com/item/%E8%94%A1%E7%BE%8E%E5%84%BF/3424632#7"),a(o)])]),g,e("p",null,[t("原文链接:"),e("a",_,[t("https://www.ximalaya.com/sound/414999846"),a(o)])]),x,e("p",null,[t("原文链接:"),e("a",k,[t("https://www.ximalaya.com/sound/415001340"),a(o)])]),v,T,e("p",null,[t("原文链接:"),e("a",A,[t("https://www.ximalaya.com/sound/415004615"),a(o)])]),E,C,e("p",null,[t("原文链接:"),e("a",I,[t("https://www.ximalaya.com/sound/415006607"),a(o)])]),q,B,D,e("p",null,[t("原文链接:"),e("a",N,[t("https://www.ximalaya.com/sound/415007539"),a(o)])]),W,e("p",null,[t("原文链接:"),e("a",j,[t("https://www.ximalaya.com/sound/415017440"),a(o)])]),P,F,e("p",null,[t("原文链接:"),e("a",H,[t("https://www.ximalaya.com/sound/415019607"),a(o)])]),L,V,e("p",null,[t("原文链接:"),e("a",M,[t("https://www.ximalaya.com/sound/415020743"),a(o)])]),z,G,e("p",null,[t("原文链接:"),e("a",O,[t("https://www.ximalaya.com/sound/415022329"),a(o)])]),S,Y,e("p",null,[t("原文链接:"),e("a",J,[t("https://www.ximalaya.com/sound/415018537"),a(o)])]),R])}const $=i(l,[["render",U],["__file","contemporary-college-english-6.html.vue"]]);export{$ as default}; diff --git a/assets/contemporary-college-english-6.html-b05e70fd.js b/assets/contemporary-college-english-6.html-cc0d8518.js similarity index 93% rename from assets/contemporary-college-english-6.html-b05e70fd.js rename to assets/contemporary-college-english-6.html-cc0d8518.js index 8fe16d01..6b30e858 100644 --- a/assets/contemporary-college-english-6.html-b05e70fd.js +++ b/assets/contemporary-college-english-6.html-cc0d8518.js @@ -1 +1 @@ -const e=JSON.parse(`{"key":"v-860c51c8","path":"/english/contemporary-college-english-6.html","title":"现代大学英语精读(第2版)第六册","lang":"zh-CN","frontmatter":{"date":"2022-09-04T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第六册 前言 全书链接:https://www.ximalaya.com/album/49468954 本册是整个系列的最后一册了,完结撒花🎉 Paper Tigers 原文链接:https://www.ximalaya.com/sound/414998175","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-6.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第六册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第六册 前言 全书链接:https://www.ximalaya.com/album/49468954 本册是整个系列的最后一册了,完结撒花🎉 Paper Tigers 原文链接:https://www.ximalaya.com/sound/414998175"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-09-04T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第六册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-09-04T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"Paper Tigers","slug":"paper-tigers","link":"#paper-tigers","children":[]},{"level":2,"title":"What Is News","slug":"what-is-news","link":"#what-is-news","children":[]},{"level":2,"title":"At War with the Planet","slug":"at-war-with-the-planet","link":"#at-war-with-the-planet","children":[]},{"level":2,"title":"How to Get the Poor off Our Conscience","slug":"how-to-get-the-poor-off-our-conscience","link":"#how-to-get-the-poor-off-our-conscience","children":[]},{"level":2,"title":"Housewifely Arts","slug":"housewifely-arts","link":"#housewifely-arts","children":[]},{"level":2,"title":"The One Against The Many","slug":"the-one-against-the-many","link":"#the-one-against-the-many","children":[]},{"level":2,"title":"Notes on the English Character","slug":"notes-on-the-english-character","link":"#notes-on-the-english-character","children":[]},{"level":2,"title":"The Death of a Pig","slug":"the-death-of-a-pig","link":"#the-death-of-a-pig","children":[]},{"level":2,"title":"Don't Eat Fortune's Cookie","slug":"don-t-eat-fortune-s-cookie","link":"#don-t-eat-fortune-s-cookie","children":[]},{"level":2,"title":"The Accidental Universe","slug":"the-accidental-universe","link":"#the-accidental-universe","children":[]},{"level":2,"title":"Rowling's Speech at Harvard","slug":"rowling-s-speech-at-harvard","link":"#rowling-s-speech-at-harvard","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5.08,"words":1524},"filePathRelative":"english/contemporary-college-english-6.md","localizedDate":"2022年9月4日","excerpt":"

现代大学英语精读(第2版)第六册

\\n

前言

\\n

全书链接:https://www.ximalaya.com/album/49468954

\\n

本册是整个系列的最后一册了,完结撒花🎉

\\n

Paper Tigers

\\n

原文链接:https://www.ximalaya.com/sound/414998175

","autoDesc":true}`);export{e as data}; +const e=JSON.parse(`{"key":"v-860c51c8","path":"/english/contemporary-college-english-6.html","title":"现代大学英语精读(第2版)第六册","lang":"zh-CN","frontmatter":{"date":"2022-09-04T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第六册 前言 全书链接:https://www.ximalaya.com/album/49468954 本册是整个系列的最后一册了,完结撒花🎉 Paper Tigers 原文链接:https://www.ximalaya.com/sound/414998175","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-6.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第六册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第六册 前言 全书链接:https://www.ximalaya.com/album/49468954 本册是整个系列的最后一册了,完结撒花🎉 Paper Tigers 原文链接:https://www.ximalaya.com/sound/414998175"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-09-04T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第六册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-09-04T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"Paper Tigers","slug":"paper-tigers","link":"#paper-tigers","children":[]},{"level":2,"title":"What Is News","slug":"what-is-news","link":"#what-is-news","children":[]},{"level":2,"title":"At War with the Planet","slug":"at-war-with-the-planet","link":"#at-war-with-the-planet","children":[]},{"level":2,"title":"How to Get the Poor off Our Conscience","slug":"how-to-get-the-poor-off-our-conscience","link":"#how-to-get-the-poor-off-our-conscience","children":[]},{"level":2,"title":"Housewifely Arts","slug":"housewifely-arts","link":"#housewifely-arts","children":[]},{"level":2,"title":"The One Against The Many","slug":"the-one-against-the-many","link":"#the-one-against-the-many","children":[]},{"level":2,"title":"Notes on the English Character","slug":"notes-on-the-english-character","link":"#notes-on-the-english-character","children":[]},{"level":2,"title":"The Death of a Pig","slug":"the-death-of-a-pig","link":"#the-death-of-a-pig","children":[]},{"level":2,"title":"Don't Eat Fortune's Cookie","slug":"don-t-eat-fortune-s-cookie","link":"#don-t-eat-fortune-s-cookie","children":[]},{"level":2,"title":"The Accidental Universe","slug":"the-accidental-universe","link":"#the-accidental-universe","children":[]},{"level":2,"title":"Rowling's Speech at Harvard","slug":"rowling-s-speech-at-harvard","link":"#rowling-s-speech-at-harvard","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5.08,"words":1524},"filePathRelative":"english/contemporary-college-english-6.md","localizedDate":"2022年9月4日","excerpt":"

现代大学英语精读(第2版)第六册

\\n

前言

\\n

全书链接:https://www.ximalaya.com/album/49468954

\\n

本册是整个系列的最后一册了,完结撒花🎉

\\n

Paper Tigers

\\n

原文链接:https://www.ximalaya.com/sound/414998175

","autoDesc":true}`);export{e as data}; diff --git a/assets/copy-code-may-not-be-guilty.html-765412c3.js b/assets/copy-code-may-not-be-guilty.html-52cea58d.js similarity index 91% rename from assets/copy-code-may-not-be-guilty.html-765412c3.js rename to assets/copy-code-may-not-be-guilty.html-52cea58d.js index a7a877b2..84f5e8ab 100644 --- a/assets/copy-code-may-not-be-guilty.html-765412c3.js +++ b/assets/copy-code-may-not-be-guilty.html-52cea58d.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-76f251d2","path":"/daily/copy-code-may-not-be-guilty.html","title":"复制代码也许不是罪","lang":"zh-CN","frontmatter":{"date":"2023-09-24T00:00:00.000Z","tag":["Daily"],"description":"复制代码也许不是罪 前言 熟悉我的人都知道,我对代码是有追求的。 正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。 我早期认为:复制代码就是菜。 后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。 而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。 编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/copy-code-may-not-be-guilty.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"复制代码也许不是罪"}],["meta",{"property":"og:description","content":"复制代码也许不是罪 前言 熟悉我的人都知道,我对代码是有追求的。 正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。 我早期认为:复制代码就是菜。 后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。 而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。 编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-24T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"复制代码也许不是罪\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-24T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"正文","slug":"正文","link":"#正文","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.51,"words":453},"filePathRelative":"daily/copy-code-may-not-be-guilty.md","localizedDate":"2023年9月24日","excerpt":"

复制代码也许不是罪

\\n

前言

\\n

熟悉我的人都知道,我对代码是有追求的。

\\n

正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。

\\n

我早期认为:复制代码就是菜。

\\n

后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

\\n

而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

\\n

编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-76f251d2","path":"/daily/copy-code-may-not-be-guilty.html","title":"复制代码也许不是罪","lang":"zh-CN","frontmatter":{"date":"2023-09-24T00:00:00.000Z","tag":["Daily"],"description":"复制代码也许不是罪 前言 熟悉我的人都知道,我对代码是有追求的。 正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。 我早期认为:复制代码就是菜。 后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。 而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。 编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/copy-code-may-not-be-guilty.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"复制代码也许不是罪"}],["meta",{"property":"og:description","content":"复制代码也许不是罪 前言 熟悉我的人都知道,我对代码是有追求的。 正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。 我早期认为:复制代码就是菜。 后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。 而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。 编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-24T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"复制代码也许不是罪\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-24T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"正文","slug":"正文","link":"#正文","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.51,"words":453},"filePathRelative":"daily/copy-code-may-not-be-guilty.md","localizedDate":"2023年9月24日","excerpt":"

复制代码也许不是罪

\\n

前言

\\n

熟悉我的人都知道,我对代码是有追求的。

\\n

正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。

\\n

我早期认为:复制代码就是菜。

\\n

后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

\\n

而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

\\n

编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/copy-code-may-not-be-guilty.html-6eb3011e.js b/assets/copy-code-may-not-be-guilty.html-5562ab70.js similarity index 97% rename from assets/copy-code-may-not-be-guilty.html-6eb3011e.js rename to assets/copy-code-may-not-be-guilty.html-5562ab70.js index f8972786..51501c5c 100644 --- a/assets/copy-code-may-not-be-guilty.html-6eb3011e.js +++ b/assets/copy-code-may-not-be-guilty.html-5562ab70.js @@ -1 +1 @@ -import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c as i,e as r,f as e}from"./app-b649ee34.js";const o={},d=e('

复制代码也许不是罪

前言

熟悉我的人都知道,我对代码是有追求的。

正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。

我早期认为:复制代码就是菜。

后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

',8),m=e('

正文

我总结了一下,主要有以下原因:
1.深刻地理解了源码与测试用例之间的关系。只相信测试用例,而不是相信源码。如果你只改源码而不是补充相应的测试代码,那所谓的有追求是盲目、片面的。

2.对具体的编码实现已经缺乏兴致了。尤其是有AI的情况下,真的是有手就会,何必要我来写呢?我负责设计,再进行 Code Review 不是更好吗?


3.要考虑时间成本。如果别人已经做了一大半,我何必重头开始?

4.提升对质量的认知。聚集于产出质量,而不仅仅是代码质量,那么你就会发现,要做的事还挺多,再考虑时间成本,需要做一个权衡取舍。

',5);function n(c,l){return t(),i("div",null,[d,r(" more "),m])}const s=a(o,[["render",n],["__file","copy-code-may-not-be-guilty.html.vue"]]);export{s as default}; +import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c as i,e as r,f as e}from"./app-a9d55428.js";const o={},d=e('

复制代码也许不是罪

前言

熟悉我的人都知道,我对代码是有追求的。

正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。

我早期认为:复制代码就是菜。

后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

',8),m=e('

正文

我总结了一下,主要有以下原因:
1.深刻地理解了源码与测试用例之间的关系。只相信测试用例,而不是相信源码。如果你只改源码而不是补充相应的测试代码,那所谓的有追求是盲目、片面的。

2.对具体的编码实现已经缺乏兴致了。尤其是有AI的情况下,真的是有手就会,何必要我来写呢?我负责设计,再进行 Code Review 不是更好吗?


3.要考虑时间成本。如果别人已经做了一大半,我何必重头开始?

4.提升对质量的认知。聚集于产出质量,而不仅仅是代码质量,那么你就会发现,要做的事还挺多,再考虑时间成本,需要做一个权衡取舍。

',5);function n(c,l){return t(),i("div",null,[d,r(" more "),m])}const s=a(o,[["render",n],["__file","copy-code-may-not-be-guilty.html.vue"]]);export{s as default}; diff --git a/assets/dont-try-to-argue-with-a-sb.html-6b8dcf17.js b/assets/dont-try-to-argue-with-a-sb.html-5919dbb8.js similarity index 98% rename from assets/dont-try-to-argue-with-a-sb.html-6b8dcf17.js rename to assets/dont-try-to-argue-with-a-sb.html-5919dbb8.js index 6927a3c5..10d275ae 100644 --- a/assets/dont-try-to-argue-with-a-sb.html-6b8dcf17.js +++ b/assets/dont-try-to-argue-with-a-sb.html-5919dbb8.js @@ -1 +1 @@ -import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as l,c as n,e as a,d as s,a as e,b as t,f as i}from"./app-b649ee34.js";const c={},d=e("h1",{id:"不要与傻逼进行争吵",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#不要与傻逼进行争吵","aria-hidden":"true"},"#"),t(" 不要与傻逼进行争吵")],-1),_=e("p",null,"这是个职场吐槽帖,也是个自我反思、情绪管理帖。",-1),h=e("p",null,[t("我在沟通上,有以下可以改正的点:"),e("br"),t(" 0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你")],-1),m=e("ol",null,[e("li",null,"我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你"),e("li",null,"你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通"),e("li",null,"你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答"),e("li",null,"最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!")],-1),u=i("

事情是这样的。

我跟素未谋面的同事在工作上要有一些配合,主要是我提供技术方案、文档给他。

对方工作能力不足就算了,时间管理也不行。8.3号一整天没来找我,8.4号白天也没来找我,快下班了,才打电话跟我说要资料。

这是第一层不满:快下班了,没什么耐心了,我不想别人打扰我周五下班。

电话中,他的语气带着责备,明显是甩锅的意味。找人办事,要别人配合还用这种语气,这是第二层不满。我开始怒他,我说,“兄弟,你冷静下。”

接着,他又跟我扯淡,我一看时间,六点钟了,是时候下班。我就说,“有什么问题发钉钉,我周一给你解决。” 他急了,他说,“你不能下班,你不能走,你要陪我!”
tmd,自己延误工期,还要我陪你加班,哪个这个道理?这是第三层不满,我有点破防了。我开始说一些不太理智、不太专业的话。

我说,“凭什么要我陪你加班?V我50,我就陪你”。他说,“你在搞笑吧,不如让领导V你50?” 
他接着说,“这个东西很急,客户今天晚上就要!” 我说,“那是你的事,你的客户,又不是我的客户。”

这两句话一出,味道就有点不对了。本来全然是他不占理的,现在他抓住的我语言上漏洞,开始要拉我下水了。

于是马上拉群、打字,向领导打小报告,一幅小学生向老师告状的模样。

这情商,这行为,我们还能好好配合?我们接下来净在群里互怒,要么是甩锅,要么是暗示对方能力不行,没有一个人在解决问题。

最后,我回去以后,还是带着不满加了班。不但自己加,还搞得我的 leader 也跟着加,让 leader 对那个人也无语,心情不好了。

这一点,我有点自责,因为我连累了我的 leader。而这也是我为什么反思并把这件事情写出来的原因,自己不爽只是自己的事,连累到他人,我就过意不去了。

我不希望这种事情再次发生!我应该能掌握事态,Let me take control of things!

下次再有这种事,一定是先把工作上的问题解决,然后再远离傻逼。不要在傻逼身上浪费时间,多说一句话、甚至只是骂一句都不值得!

想一想,已经几年没跟人因为工作的事情跟人发生明面上的不愉快了。我的情绪管理能力似乎下降了,这也是个契机,让我重新对这方面重视起来。

经验上看,除了开头的句式转换以外,其实有一个心法值得学习。那就是永远不要跟人在明面上闹不愉快

同时,结合“阳奉阴违”的技巧,你不用去做口舌之争,你去做你的事就行了。
比如,我准备下班了,对方说,“你不要下班,你不能走。” 我可以回复,“好的,我不走,我上个厕所。” 然后我就挂掉电话,下班走人,这不是很简单吗?何必反问他“为什么我不能走?”没有意义啊!

总之,明面上不与人闹翻,保持自己情绪的稳定,一方面是职业素养体现,另一方面也是自己涵养的体现。
还有一方面,那就是有利于身心健康——生气是会对身体器官造成伤害的,如高血压、胃疼、肠胃不适!

",18);function f(b,B){const o=r("BiliBili");return l(),n("div",null,[d,_,h,m,a(" more "),u,s(o,{bvid:"BV1Yh4y1r7k8"})])}const x=p(c,[["render",f],["__file","dont-try-to-argue-with-a-sb.html.vue"]]);export{x as default}; +import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as l,c as n,e as a,d as s,a as e,b as t,f as i}from"./app-a9d55428.js";const c={},d=e("h1",{id:"不要与傻逼进行争吵",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#不要与傻逼进行争吵","aria-hidden":"true"},"#"),t(" 不要与傻逼进行争吵")],-1),_=e("p",null,"这是个职场吐槽帖,也是个自我反思、情绪管理帖。",-1),h=e("p",null,[t("我在沟通上,有以下可以改正的点:"),e("br"),t(" 0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你")],-1),m=e("ol",null,[e("li",null,"我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你"),e("li",null,"你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通"),e("li",null,"你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答"),e("li",null,"最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!")],-1),u=i("

事情是这样的。

我跟素未谋面的同事在工作上要有一些配合,主要是我提供技术方案、文档给他。

对方工作能力不足就算了,时间管理也不行。8.3号一整天没来找我,8.4号白天也没来找我,快下班了,才打电话跟我说要资料。

这是第一层不满:快下班了,没什么耐心了,我不想别人打扰我周五下班。

电话中,他的语气带着责备,明显是甩锅的意味。找人办事,要别人配合还用这种语气,这是第二层不满。我开始怒他,我说,“兄弟,你冷静下。”

接着,他又跟我扯淡,我一看时间,六点钟了,是时候下班。我就说,“有什么问题发钉钉,我周一给你解决。” 他急了,他说,“你不能下班,你不能走,你要陪我!”
tmd,自己延误工期,还要我陪你加班,哪个这个道理?这是第三层不满,我有点破防了。我开始说一些不太理智、不太专业的话。

我说,“凭什么要我陪你加班?V我50,我就陪你”。他说,“你在搞笑吧,不如让领导V你50?” 
他接着说,“这个东西很急,客户今天晚上就要!” 我说,“那是你的事,你的客户,又不是我的客户。”

这两句话一出,味道就有点不对了。本来全然是他不占理的,现在他抓住的我语言上漏洞,开始要拉我下水了。

于是马上拉群、打字,向领导打小报告,一幅小学生向老师告状的模样。

这情商,这行为,我们还能好好配合?我们接下来净在群里互怒,要么是甩锅,要么是暗示对方能力不行,没有一个人在解决问题。

最后,我回去以后,还是带着不满加了班。不但自己加,还搞得我的 leader 也跟着加,让 leader 对那个人也无语,心情不好了。

这一点,我有点自责,因为我连累了我的 leader。而这也是我为什么反思并把这件事情写出来的原因,自己不爽只是自己的事,连累到他人,我就过意不去了。

我不希望这种事情再次发生!我应该能掌握事态,Let me take control of things!

下次再有这种事,一定是先把工作上的问题解决,然后再远离傻逼。不要在傻逼身上浪费时间,多说一句话、甚至只是骂一句都不值得!

想一想,已经几年没跟人因为工作的事情跟人发生明面上的不愉快了。我的情绪管理能力似乎下降了,这也是个契机,让我重新对这方面重视起来。

经验上看,除了开头的句式转换以外,其实有一个心法值得学习。那就是永远不要跟人在明面上闹不愉快

同时,结合“阳奉阴违”的技巧,你不用去做口舌之争,你去做你的事就行了。
比如,我准备下班了,对方说,“你不要下班,你不能走。” 我可以回复,“好的,我不走,我上个厕所。” 然后我就挂掉电话,下班走人,这不是很简单吗?何必反问他“为什么我不能走?”没有意义啊!

总之,明面上不与人闹翻,保持自己情绪的稳定,一方面是职业素养体现,另一方面也是自己涵养的体现。
还有一方面,那就是有利于身心健康——生气是会对身体器官造成伤害的,如高血压、胃疼、肠胃不适!

",18);function f(b,B){const o=r("BiliBili");return l(),n("div",null,[d,_,h,m,a(" more "),u,s(o,{bvid:"BV1Yh4y1r7k8"})])}const x=p(c,[["render",f],["__file","dont-try-to-argue-with-a-sb.html.vue"]]);export{x as default}; diff --git a/assets/dont-try-to-argue-with-a-sb.html-71bbf5fd.js b/assets/dont-try-to-argue-with-a-sb.html-c2c1581f.js similarity index 90% rename from assets/dont-try-to-argue-with-a-sb.html-71bbf5fd.js rename to assets/dont-try-to-argue-with-a-sb.html-c2c1581f.js index fa936da0..26fdc01d 100644 --- a/assets/dont-try-to-argue-with-a-sb.html-71bbf5fd.js +++ b/assets/dont-try-to-argue-with-a-sb.html-c2c1581f.js @@ -1 +1 @@ -const t=JSON.parse('{"key":"v-f47f129e","path":"/daily/dont-try-to-argue-with-a-sb.html","title":"不要与傻逼进行争吵","lang":"zh-CN","frontmatter":{"date":"2023-08-04T00:00:00.000Z","tag":["Daily","Emotion","Working Experience","Video"],"description":"不要与傻逼进行争吵 这是个职场吐槽帖,也是个自我反思、情绪管理帖。 我在沟通上,有以下可以改正的点: 0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/dont-try-to-argue-with-a-sb.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"不要与傻逼进行争吵"}],["meta",{"property":"og:description","content":"不要与傻逼进行争吵 这是个职场吐槽帖,也是个自我反思、情绪管理帖。 我在沟通上,有以下可以改正的点: 0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Emotion"}],["meta",{"property":"article:tag","content":"Working Experience"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-04T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"不要与傻逼进行争吵\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-04T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.92,"words":1176},"filePathRelative":"daily/dont-try-to-argue-with-a-sb.md","localizedDate":"2023年8月4日","excerpt":"

不要与傻逼进行争吵

\\n

这是个职场吐槽帖,也是个自我反思、情绪管理帖。

\\n

我在沟通上,有以下可以改正的点:
\\n0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你

\\n
    \\n
  1. 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你
  2. \\n
  3. 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通
  4. \\n
  5. 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答
  6. \\n
  7. 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!
  8. \\n
\\n","autoDesc":true}');export{t as data}; +const t=JSON.parse('{"key":"v-f47f129e","path":"/daily/dont-try-to-argue-with-a-sb.html","title":"不要与傻逼进行争吵","lang":"zh-CN","frontmatter":{"date":"2023-08-04T00:00:00.000Z","tag":["Daily","Emotion","Working Experience","Video"],"description":"不要与傻逼进行争吵 这是个职场吐槽帖,也是个自我反思、情绪管理帖。 我在沟通上,有以下可以改正的点: 0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/dont-try-to-argue-with-a-sb.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"不要与傻逼进行争吵"}],["meta",{"property":"og:description","content":"不要与傻逼进行争吵 这是个职场吐槽帖,也是个自我反思、情绪管理帖。 我在沟通上,有以下可以改正的点: 0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Emotion"}],["meta",{"property":"article:tag","content":"Working Experience"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-04T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"不要与傻逼进行争吵\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-04T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.92,"words":1176},"filePathRelative":"daily/dont-try-to-argue-with-a-sb.md","localizedDate":"2023年8月4日","excerpt":"

不要与傻逼进行争吵

\\n

这是个职场吐槽帖,也是个自我反思、情绪管理帖。

\\n

我在沟通上,有以下可以改正的点:
\\n0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你

\\n
    \\n
  1. 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你
  2. \\n
  3. 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通
  4. \\n
  5. 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答
  6. \\n
  7. 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!
  8. \\n
\\n","autoDesc":true}');export{t as data}; diff --git a/assets/everyone-can-learn-english-1-overview.html-f280cd0b.js b/assets/everyone-can-learn-english-1-overview.html-52c2cad5.js similarity index 92% rename from assets/everyone-can-learn-english-1-overview.html-f280cd0b.js rename to assets/everyone-can-learn-english-1-overview.html-52c2cad5.js index ffcffbab..ef3caa5e 100644 --- a/assets/everyone-can-learn-english-1-overview.html-f280cd0b.js +++ b/assets/everyone-can-learn-english-1-overview.html-52c2cad5.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-246f4f17","path":"/english/everyone-can-learn-english-1-overview.html","title":"人人都能学会的英语1:开篇","lang":"zh-CN","frontmatter":{"date":"2022-06-23T00:00:00.000Z","tag":"English","description":"人人都能学会的英语1:开篇 为什么学 不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。 也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语: 出国 去外企 有国际化需求 学习专业领域的前沿知识 我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-1-overview.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语1:开篇"}],["meta",{"property":"og:description","content":"人人都能学会的英语1:开篇 为什么学 不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。 也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语: 出国 去外企 有国际化需求 学习专业领域的前沿知识 我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-23T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语1:开篇\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-23T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"为什么学","slug":"为什么学","link":"#为什么学","children":[]},{"level":2,"title":"怎么学","slug":"怎么学","link":"#怎么学","children":[{"level":3,"title":"建设心态","slug":"建设心态","link":"#建设心态","children":[]},{"level":3,"title":"提升认知","slug":"提升认知","link":"#提升认知","children":[]},{"level":3,"title":"明确意义","slug":"明确意义","link":"#明确意义","children":[]},{"level":3,"title":"制定计划","slug":"制定计划","link":"#制定计划","children":[]},{"level":3,"title":"培养习惯","slug":"培养习惯","link":"#培养习惯","children":[]},{"level":3,"title":"注重方法","slug":"注重方法","link":"#注重方法","children":[]}]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.46,"words":1337},"filePathRelative":"english/everyone-can-learn-english-1-overview.md","localizedDate":"2022年6月23日","excerpt":"

人人都能学会的英语1:开篇

\\n

为什么学

\\n

不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。

\\n

也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语:

\\n
    \\n
  • 出国
  • \\n
  • 去外企
  • \\n
  • 有国际化需求
  • \\n
  • 学习专业领域的前沿知识
  • \\n
\\n

我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-246f4f17","path":"/english/everyone-can-learn-english-1-overview.html","title":"人人都能学会的英语1:开篇","lang":"zh-CN","frontmatter":{"date":"2022-06-23T00:00:00.000Z","tag":"English","description":"人人都能学会的英语1:开篇 为什么学 不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。 也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语: 出国 去外企 有国际化需求 学习专业领域的前沿知识 我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-1-overview.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语1:开篇"}],["meta",{"property":"og:description","content":"人人都能学会的英语1:开篇 为什么学 不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。 也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语: 出国 去外企 有国际化需求 学习专业领域的前沿知识 我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-23T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语1:开篇\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-23T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"为什么学","slug":"为什么学","link":"#为什么学","children":[]},{"level":2,"title":"怎么学","slug":"怎么学","link":"#怎么学","children":[{"level":3,"title":"建设心态","slug":"建设心态","link":"#建设心态","children":[]},{"level":3,"title":"提升认知","slug":"提升认知","link":"#提升认知","children":[]},{"level":3,"title":"明确意义","slug":"明确意义","link":"#明确意义","children":[]},{"level":3,"title":"制定计划","slug":"制定计划","link":"#制定计划","children":[]},{"level":3,"title":"培养习惯","slug":"培养习惯","link":"#培养习惯","children":[]},{"level":3,"title":"注重方法","slug":"注重方法","link":"#注重方法","children":[]}]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.46,"words":1337},"filePathRelative":"english/everyone-can-learn-english-1-overview.md","localizedDate":"2022年6月23日","excerpt":"

人人都能学会的英语1:开篇

\\n

为什么学

\\n

不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。

\\n

也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语:

\\n
    \\n
  • 出国
  • \\n
  • 去外企
  • \\n
  • 有国际化需求
  • \\n
  • 学习专业领域的前沿知识
  • \\n
\\n

我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。

","autoDesc":true}');export{e as data}; diff --git a/assets/everyone-can-learn-english-1-overview.html-50a1c72e.js b/assets/everyone-can-learn-english-1-overview.html-a4b93650.js similarity index 98% rename from assets/everyone-can-learn-english-1-overview.html-50a1c72e.js rename to assets/everyone-can-learn-english-1-overview.html-a4b93650.js index 4da33f52..39aa2400 100644 --- a/assets/everyone-can-learn-english-1-overview.html-50a1c72e.js +++ b/assets/everyone-can-learn-english-1-overview.html-a4b93650.js @@ -1 +1 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as a,c as i,f as r}from"./app-b649ee34.js";const h={},l=r('

人人都能学会的英语1:开篇

为什么学

不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。

也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语:

  • 出国
  • 去外企
  • 有国际化需求
  • 学习专业领域的前沿知识

我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。

此时也许有人会担心,自己已错过了最佳的学习时机,现在太迟了吧?非也。种一棵树最好的时机是10年前,其次是现在。如果把人生当成马拉松,那么现在开始,一定来得及,绝不会晚。记住,Better late than never!

怎么学

关于怎么学的问题,我认为需要内外兼修。

建设心态

在意识到、并承认掌握英语的必要性后,我们需要真正地相信:现在行动并不晚。

如果这点不能相信,那说什么都没用,因为人会找各种借口让自己放弃,如“现在没时间,还有更重要的事”、“性价比太低了”、“以后再说吧”、“用中文也行”。如果是这样,那事情注定是要失败的。

其次,要学会耐心。人们常常高估一个月所能做的事,却低估一年所能做的事。不要浮躁,学会接受每天进步一点点:

有了耐心,才能在整个过程中,抵制住营销号“神奇方法”的诱惑。记住,耐心是美德,这在别的领域也适用。

与此同时,在学习过程中,注意建立自己的信心。从怀疑自己是否能,到相信自己能,最后内化成不再需要强调这件事。

提升认知

一定要搞明白:英语是技能,不只是知识。技能意味着,不仅要输入,还要输出,且需要反复地练习。

所以,在学习过程中,一定要让输入与输出闭环:如果想提高听力,就要辅以口语练习;如果想提高阅读能力,就要做相应的写作练习。

整个过程中,强调的是实践,是行动力,而不是记忆力。不要妄想把大量的知识装入脑子里——那是没用的,因为它不属于你。只有经过练习、能说出来或写出来的东西,才是你的,才会内化成自己的能力。

以上认知很重要,如果不能达成共识,那后面的方法对你是无效的。

明确意义

需要问一下自己,英语对自己而言到底是什么?

是考试、考证上分的手段,还是升职加薪的工具,抑或是丰富自己、了解另一种文化、开阔视野的方式?

上面只是举例,并且例子之间并不冲突,是可以兼容的。

总之,对这个问题的回答,反映了个人动机,对行动力有很大的影响。

制定计划

整个学习过程是超过一个月的,对于这种时长的事件,是需要制定计划的。

说直白了,就是要拆分目标,评估工作量,然后按计划执行,并能在实践中结合自身情况动态调整。这种能力,不仅是学英语,学任何东西都是适用的。

培养习惯

注意培养自己的习惯,利用惯性帮助自己坚持,减少半途而废的可能。

建议固定时间、固定地点来学习,可以结合自己的日常作息规律,养成一个新的习惯。如每天早上起来,或晚上洗完澡后,固定抽时间来学习。

注重方法

前面说了那么多,都是内功,放在前面,是因为我认为心态建设、认知及习惯比方法更重要。

至于学习方法,因学习内容而异。按照知识体系划可大致分为:

  • 基础知识:
    • 音标
    • 单词
    • 语法
  • 综合能力
    • 听说
    • 读写

基础知识是一定要掌握的,而对于综合能力,基于实用主义,不同目的可有不同的侧重。简单来说就是:

  • 如果需要利用英文资料学习、工作,或想阅读英文原著,或只需与人进行文字交流,应侧重读写
  • 如果需要与人进行即时言语沟通,或想看“生肉”,或想听英文有声读物,应侧重听说

今天主要是作为开篇,做个介绍,具体的方法,会在后续的内容中逐步展开。

',38),p=[l];function d(n,t){return a(),i("div",null,p)}const o=e(h,[["render",d],["__file","everyone-can-learn-english-1-overview.html.vue"]]);export{o as default}; +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as a,c as i,f as r}from"./app-a9d55428.js";const h={},l=r('

人人都能学会的英语1:开篇

为什么学

不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。

也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语:

  • 出国
  • 去外企
  • 有国际化需求
  • 学习专业领域的前沿知识

我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。

此时也许有人会担心,自己已错过了最佳的学习时机,现在太迟了吧?非也。种一棵树最好的时机是10年前,其次是现在。如果把人生当成马拉松,那么现在开始,一定来得及,绝不会晚。记住,Better late than never!

怎么学

关于怎么学的问题,我认为需要内外兼修。

建设心态

在意识到、并承认掌握英语的必要性后,我们需要真正地相信:现在行动并不晚。

如果这点不能相信,那说什么都没用,因为人会找各种借口让自己放弃,如“现在没时间,还有更重要的事”、“性价比太低了”、“以后再说吧”、“用中文也行”。如果是这样,那事情注定是要失败的。

其次,要学会耐心。人们常常高估一个月所能做的事,却低估一年所能做的事。不要浮躁,学会接受每天进步一点点:

有了耐心,才能在整个过程中,抵制住营销号“神奇方法”的诱惑。记住,耐心是美德,这在别的领域也适用。

与此同时,在学习过程中,注意建立自己的信心。从怀疑自己是否能,到相信自己能,最后内化成不再需要强调这件事。

提升认知

一定要搞明白:英语是技能,不只是知识。技能意味着,不仅要输入,还要输出,且需要反复地练习。

所以,在学习过程中,一定要让输入与输出闭环:如果想提高听力,就要辅以口语练习;如果想提高阅读能力,就要做相应的写作练习。

整个过程中,强调的是实践,是行动力,而不是记忆力。不要妄想把大量的知识装入脑子里——那是没用的,因为它不属于你。只有经过练习、能说出来或写出来的东西,才是你的,才会内化成自己的能力。

以上认知很重要,如果不能达成共识,那后面的方法对你是无效的。

明确意义

需要问一下自己,英语对自己而言到底是什么?

是考试、考证上分的手段,还是升职加薪的工具,抑或是丰富自己、了解另一种文化、开阔视野的方式?

上面只是举例,并且例子之间并不冲突,是可以兼容的。

总之,对这个问题的回答,反映了个人动机,对行动力有很大的影响。

制定计划

整个学习过程是超过一个月的,对于这种时长的事件,是需要制定计划的。

说直白了,就是要拆分目标,评估工作量,然后按计划执行,并能在实践中结合自身情况动态调整。这种能力,不仅是学英语,学任何东西都是适用的。

培养习惯

注意培养自己的习惯,利用惯性帮助自己坚持,减少半途而废的可能。

建议固定时间、固定地点来学习,可以结合自己的日常作息规律,养成一个新的习惯。如每天早上起来,或晚上洗完澡后,固定抽时间来学习。

注重方法

前面说了那么多,都是内功,放在前面,是因为我认为心态建设、认知及习惯比方法更重要。

至于学习方法,因学习内容而异。按照知识体系划可大致分为:

  • 基础知识:
    • 音标
    • 单词
    • 语法
  • 综合能力
    • 听说
    • 读写

基础知识是一定要掌握的,而对于综合能力,基于实用主义,不同目的可有不同的侧重。简单来说就是:

  • 如果需要利用英文资料学习、工作,或想阅读英文原著,或只需与人进行文字交流,应侧重读写
  • 如果需要与人进行即时言语沟通,或想看“生肉”,或想听英文有声读物,应侧重听说

今天主要是作为开篇,做个介绍,具体的方法,会在后续的内容中逐步展开。

',38),p=[l];function d(n,t){return a(),i("div",null,p)}const o=e(h,[["render",d],["__file","everyone-can-learn-english-1-overview.html.vue"]]);export{o as default}; diff --git a/assets/everyone-can-learn-english-2-pronunciation.html-991c3fa5.js b/assets/everyone-can-learn-english-2-pronunciation.html-796320a9.js similarity index 97% rename from assets/everyone-can-learn-english-2-pronunciation.html-991c3fa5.js rename to assets/everyone-can-learn-english-2-pronunciation.html-796320a9.js index a6f7d80e..35f6c825 100644 --- a/assets/everyone-can-learn-english-2-pronunciation.html-991c3fa5.js +++ b/assets/everyone-can-learn-english-2-pronunciation.html-796320a9.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as t,c as h,a as e,b as a,d as r,f as i}from"./app-b649ee34.js";const c={},s=i('

人人都能学会的英语2:音标

方法

音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。

我推荐根据赖世雄的《美语音标》进行学习:

  • 在微信公众号 常春藤英语集团 买相关书籍
  • 在 喜马拉雅 或 B站 听相关音频,进行跟读练习
  • 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格

有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。

我给出了我之前的学习进度记录,仅供参考
image.png

',7),p={href:"https://www.bilibili.com/video/BV1qE411v7oE?vd_source=046b87a1b6f0cc325266cccbb4f307fa",target:"_blank",rel:"noopener noreferrer"},d=e("br",null,null,-1),_=e("p",null,"再给出关于音标的一些资料:",-1),u={href:"http://yinbiao.tingclass.net/show-16-9-1.html",target:"_blank",rel:"noopener noreferrer"},f={href:"https://new.qq.com/omn/20190106/20190106G062W8.html",target:"_blank",rel:"noopener noreferrer"},m=i('

讨论

这里我补充几点,虽然不是核心,却与主题息息相关:

  1. 是否要买实体书?
  2. 为什么是美语,而不是“英语”
  3. 关于口音(accent)

建议买实体书

现在网络这么发达,信息随处可得,所以很可能你不愿意买实体书。

这样也行,但我仍然建议买书,反正我买了全套,因为我认为从某种意义上讲,实体书有助于提升行动力。还记得前面我们讲到过,整个学习过程强调的是实践,行动力或执行力是很重要的。而购买的动作,本身就是执行力的一种表现。

而相对的,白嫖的行为背后,隐含着不愿意付出的意味,这种思维本身是不可取的。当然,这也许是我过度解读,你可以继续白嫖。

不想买实体书的另一个原因是,书多了不方便,比如搬家很麻烦。对这个问题,我是这样处理的,仅供参考:

  • 捐书
  • 送人

美语是主流

也许有人认为英音更好听,某些时候,我也赞同这个观点。但我们还是实用主义,现在西方世界谁说话最大声,大家都清楚。毕竟“日不落”已然衰落,现在还是美刀更好使(还在说刀的事,手动滑稽)!所以,美语才是主流。

一定要学英音的,建议通关后重修发音,或现在就另找门派学习。

不要纠结口音

在国内的网络平台上,很常见的场景是一群人对某个中国人的口音评头论足,说什么“这个不正宗、不地道啊”,“你这个也不像美音呀”、”工地散装英语哈哈哈“。

我都不知道这些人在纠结什么。谁说话没口音呢?你作为中国人,能跟新闻联播主持人一样说普通话吗?你不也带点地方特色?就算是美国人,不同的地区,也有不同的口音,你说谁才是正宗的呢?

语言最重要的功能是沟通,能让别人听懂是首要需求——如果你的口音并不影响别人听懂并理解你,那就不用纠结它。这也是前面为什么我建议下载翻译软件的原因,让机器来判断你发音是否正确,免去担心自己口音的困扰。

总结一下,今天分享了音标学习的方法与经验,后面将分享单词的学习。

',17);function b(g,x){const n=l("ExternalLinkIcon");return t(),h("div",null,[s,e("p",null,[a("学完之后,如果有兴趣,可以了解下进阶内容。American pronunciation workshop:"),e("a",p,[a("https://www.bilibili.com/video/BV1qE411v7oE"),r(n)]),d,a(" (我特意找了没有字幕的视频,建议就这样看。如果是侧重读写的同学,直接跳过就好)")]),_,e("ul",null,[e("li",null,[e("a",u,[a("音标发音大全 48个英语音标表(IPA,DJ音标,KK音标对照表)"),r(n)])]),e("li",null,[e("a",f,[a("一文理解IPA音标、DJ音标、KK音标的区别"),r(n)])])]),m])}const k=o(c,[["render",b],["__file","everyone-can-learn-english-2-pronunciation.html.vue"]]);export{k as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as t,c as h,a as e,b as a,d as r,f as i}from"./app-a9d55428.js";const c={},s=i('

人人都能学会的英语2:音标

方法

音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。

我推荐根据赖世雄的《美语音标》进行学习:

  • 在微信公众号 常春藤英语集团 买相关书籍
  • 在 喜马拉雅 或 B站 听相关音频,进行跟读练习
  • 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格

有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。

我给出了我之前的学习进度记录,仅供参考
image.png

',7),p={href:"https://www.bilibili.com/video/BV1qE411v7oE?vd_source=046b87a1b6f0cc325266cccbb4f307fa",target:"_blank",rel:"noopener noreferrer"},d=e("br",null,null,-1),_=e("p",null,"再给出关于音标的一些资料:",-1),u={href:"http://yinbiao.tingclass.net/show-16-9-1.html",target:"_blank",rel:"noopener noreferrer"},f={href:"https://new.qq.com/omn/20190106/20190106G062W8.html",target:"_blank",rel:"noopener noreferrer"},m=i('

讨论

这里我补充几点,虽然不是核心,却与主题息息相关:

  1. 是否要买实体书?
  2. 为什么是美语,而不是“英语”
  3. 关于口音(accent)

建议买实体书

现在网络这么发达,信息随处可得,所以很可能你不愿意买实体书。

这样也行,但我仍然建议买书,反正我买了全套,因为我认为从某种意义上讲,实体书有助于提升行动力。还记得前面我们讲到过,整个学习过程强调的是实践,行动力或执行力是很重要的。而购买的动作,本身就是执行力的一种表现。

而相对的,白嫖的行为背后,隐含着不愿意付出的意味,这种思维本身是不可取的。当然,这也许是我过度解读,你可以继续白嫖。

不想买实体书的另一个原因是,书多了不方便,比如搬家很麻烦。对这个问题,我是这样处理的,仅供参考:

  • 捐书
  • 送人

美语是主流

也许有人认为英音更好听,某些时候,我也赞同这个观点。但我们还是实用主义,现在西方世界谁说话最大声,大家都清楚。毕竟“日不落”已然衰落,现在还是美刀更好使(还在说刀的事,手动滑稽)!所以,美语才是主流。

一定要学英音的,建议通关后重修发音,或现在就另找门派学习。

不要纠结口音

在国内的网络平台上,很常见的场景是一群人对某个中国人的口音评头论足,说什么“这个不正宗、不地道啊”,“你这个也不像美音呀”、”工地散装英语哈哈哈“。

我都不知道这些人在纠结什么。谁说话没口音呢?你作为中国人,能跟新闻联播主持人一样说普通话吗?你不也带点地方特色?就算是美国人,不同的地区,也有不同的口音,你说谁才是正宗的呢?

语言最重要的功能是沟通,能让别人听懂是首要需求——如果你的口音并不影响别人听懂并理解你,那就不用纠结它。这也是前面为什么我建议下载翻译软件的原因,让机器来判断你发音是否正确,免去担心自己口音的困扰。

总结一下,今天分享了音标学习的方法与经验,后面将分享单词的学习。

',17);function b(g,x){const n=l("ExternalLinkIcon");return t(),h("div",null,[s,e("p",null,[a("学完之后,如果有兴趣,可以了解下进阶内容。American pronunciation workshop:"),e("a",p,[a("https://www.bilibili.com/video/BV1qE411v7oE"),r(n)]),d,a(" (我特意找了没有字幕的视频,建议就这样看。如果是侧重读写的同学,直接跳过就好)")]),_,e("ul",null,[e("li",null,[e("a",u,[a("音标发音大全 48个英语音标表(IPA,DJ音标,KK音标对照表)"),r(n)])]),e("li",null,[e("a",f,[a("一文理解IPA音标、DJ音标、KK音标的区别"),r(n)])])]),m])}const k=o(c,[["render",b],["__file","everyone-can-learn-english-2-pronunciation.html.vue"]]);export{k as default}; diff --git a/assets/everyone-can-learn-english-2-pronunciation.html-3bc03470.js b/assets/everyone-can-learn-english-2-pronunciation.html-f7ca6655.js similarity index 92% rename from assets/everyone-can-learn-english-2-pronunciation.html-3bc03470.js rename to assets/everyone-can-learn-english-2-pronunciation.html-f7ca6655.js index a261a0f9..4699ed17 100644 --- a/assets/everyone-can-learn-english-2-pronunciation.html-3bc03470.js +++ b/assets/everyone-can-learn-english-2-pronunciation.html-f7ca6655.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-3ac0474c","path":"/english/everyone-can-learn-english-2-pronunciation.html","title":"人人都能学会的英语2:音标","lang":"zh-CN","frontmatter":{"date":"2022-06-26T00:00:00.000Z","tag":"English","description":"人人都能学会的英语2:音标 方法 音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。 我推荐根据赖世雄的《美语音标》进行学习: 在微信公众号 常春藤英语集团 买相关书籍 在 喜马拉雅 或 B站 听相关音频,进行跟读练习 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格 有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-2-pronunciation.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语2:音标"}],["meta",{"property":"og:description","content":"人人都能学会的英语2:音标 方法 音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。 我推荐根据赖世雄的《美语音标》进行学习: 在微信公众号 常春藤英语集团 买相关书籍 在 喜马拉雅 或 B站 听相关音频,进行跟读练习 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格 有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-26T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语2:音标\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-26T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"方法","slug":"方法","link":"#方法","children":[]},{"level":2,"title":"讨论","slug":"讨论","link":"#讨论","children":[{"level":3,"title":"建议买实体书","slug":"建议买实体书","link":"#建议买实体书","children":[]},{"level":3,"title":"美语是主流","slug":"美语是主流","link":"#美语是主流","children":[]},{"level":3,"title":"不要纠结口音","slug":"不要纠结口音","link":"#不要纠结口音","children":[]}]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.61,"words":1083},"filePathRelative":"english/everyone-can-learn-english-2-pronunciation.md","localizedDate":"2022年6月26日","excerpt":"

人人都能学会的英语2:音标

\\n

方法

\\n

音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。

\\n

我推荐根据赖世雄的《美语音标》进行学习:

\\n
    \\n
  • 在微信公众号 常春藤英语集团 买相关书籍
  • \\n
  • 在 喜马拉雅 或 B站 听相关音频,进行跟读练习
  • \\n
  • 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格
  • \\n
\\n

有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-3ac0474c","path":"/english/everyone-can-learn-english-2-pronunciation.html","title":"人人都能学会的英语2:音标","lang":"zh-CN","frontmatter":{"date":"2022-06-26T00:00:00.000Z","tag":"English","description":"人人都能学会的英语2:音标 方法 音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。 我推荐根据赖世雄的《美语音标》进行学习: 在微信公众号 常春藤英语集团 买相关书籍 在 喜马拉雅 或 B站 听相关音频,进行跟读练习 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格 有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-2-pronunciation.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语2:音标"}],["meta",{"property":"og:description","content":"人人都能学会的英语2:音标 方法 音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。 我推荐根据赖世雄的《美语音标》进行学习: 在微信公众号 常春藤英语集团 买相关书籍 在 喜马拉雅 或 B站 听相关音频,进行跟读练习 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格 有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-26T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语2:音标\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-26T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"方法","slug":"方法","link":"#方法","children":[]},{"level":2,"title":"讨论","slug":"讨论","link":"#讨论","children":[{"level":3,"title":"建议买实体书","slug":"建议买实体书","link":"#建议买实体书","children":[]},{"level":3,"title":"美语是主流","slug":"美语是主流","link":"#美语是主流","children":[]},{"level":3,"title":"不要纠结口音","slug":"不要纠结口音","link":"#不要纠结口音","children":[]}]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.61,"words":1083},"filePathRelative":"english/everyone-can-learn-english-2-pronunciation.md","localizedDate":"2022年6月26日","excerpt":"

人人都能学会的英语2:音标

\\n

方法

\\n

音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。

\\n

我推荐根据赖世雄的《美语音标》进行学习:

\\n
    \\n
  • 在微信公众号 常春藤英语集团 买相关书籍
  • \\n
  • 在 喜马拉雅 或 B站 听相关音频,进行跟读练习
  • \\n
  • 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格
  • \\n
\\n

有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。

","autoDesc":true}');export{e as data}; diff --git a/assets/everyone-can-learn-english-3-words.html-6f098cd3.js b/assets/everyone-can-learn-english-3-words.html-c288ffa2.js similarity index 90% rename from assets/everyone-can-learn-english-3-words.html-6f098cd3.js rename to assets/everyone-can-learn-english-3-words.html-c288ffa2.js index 4e33f8eb..877f6fb4 100644 --- a/assets/everyone-can-learn-english-3-words.html-6f098cd3.js +++ b/assets/everyone-can-learn-english-3-words.html-c288ffa2.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-58a409d7","path":"/english/everyone-can-learn-english-3-words.html","title":"人人都能学会的英语3:单词","lang":"zh-CN","frontmatter":{"date":"2022-06-27T00:00:00.000Z","tag":"English","description":"人人都能学会的英语3:单词 方法 单词是听说读写的基础,是有必要花时间去学习的。 然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议: 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。 只推荐利用词根记忆(当然并非百分百有效) 学习软件:欧路词典——免费,全平台通用","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-3-words.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语3:单词"}],["meta",{"property":"og:description","content":"人人都能学会的英语3:单词 方法 单词是听说读写的基础,是有必要花时间去学习的。 然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议: 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。 只推荐利用词根记忆(当然并非百分百有效) 学习软件:欧路词典——免费,全平台通用"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-27T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语3:单词\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-27T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"方法","slug":"方法","link":"#方法","children":[]},{"level":2,"title":"提醒","slug":"提醒","link":"#提醒","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4,"words":1200},"filePathRelative":"english/everyone-can-learn-english-3-words.md","localizedDate":"2022年6月27日","excerpt":"

人人都能学会的英语3:单词

\\n

方法

\\n

单词是听说读写的基础,是有必要花时间去学习的。

\\n

然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议:

\\n
    \\n
  1. 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握
  2. \\n
  3. 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。
  4. \\n
  5. 只推荐利用词根记忆(当然并非百分百有效)
  6. \\n
  7. 学习软件:欧路词典——免费,全平台通用
  8. \\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-58a409d7","path":"/english/everyone-can-learn-english-3-words.html","title":"人人都能学会的英语3:单词","lang":"zh-CN","frontmatter":{"date":"2022-06-27T00:00:00.000Z","tag":"English","description":"人人都能学会的英语3:单词 方法 单词是听说读写的基础,是有必要花时间去学习的。 然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议: 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。 只推荐利用词根记忆(当然并非百分百有效) 学习软件:欧路词典——免费,全平台通用","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-3-words.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语3:单词"}],["meta",{"property":"og:description","content":"人人都能学会的英语3:单词 方法 单词是听说读写的基础,是有必要花时间去学习的。 然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议: 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。 只推荐利用词根记忆(当然并非百分百有效) 学习软件:欧路词典——免费,全平台通用"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-27T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语3:单词\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-27T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"方法","slug":"方法","link":"#方法","children":[]},{"level":2,"title":"提醒","slug":"提醒","link":"#提醒","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4,"words":1200},"filePathRelative":"english/everyone-can-learn-english-3-words.md","localizedDate":"2022年6月27日","excerpt":"

人人都能学会的英语3:单词

\\n

方法

\\n

单词是听说读写的基础,是有必要花时间去学习的。

\\n

然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议:

\\n
    \\n
  1. 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握
  2. \\n
  3. 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。
  4. \\n
  5. 只推荐利用词根记忆(当然并非百分百有效)
  6. \\n
  7. 学习软件:欧路词典——免费,全平台通用
  8. \\n
","autoDesc":true}');export{e as data}; diff --git a/assets/everyone-can-learn-english-3-words.html-10909866.js b/assets/everyone-can-learn-english-3-words.html-d680e9c6.js similarity index 98% rename from assets/everyone-can-learn-english-3-words.html-10909866.js rename to assets/everyone-can-learn-english-3-words.html-d680e9c6.js index 9fc5afb5..a5be207e 100644 --- a/assets/everyone-can-learn-english-3-words.html-10909866.js +++ b/assets/everyone-can-learn-english-3-words.html-d680e9c6.js @@ -1 +1 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as a,o as s,c as p,a as r,b as e,d as l,w as c,f as i}from"./app-b649ee34.js";const d={},h=i('

人人都能学会的英语3:单词

方法

单词是听说读写的基础,是有必要花时间去学习的。

然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议:

  1. 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握
  2. 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。
  3. 只推荐利用词根记忆(当然并非百分百有效)
  4. 学习软件:欧路词典——免费,全平台通用

以手机欧路词典为例,打开后,点击“学习”,选择适合自己的单词本(或先做测试让软件来推荐),进行学习即可。
image.png
注意结合个人情况,进行学习设置,例如学习新词数量及、复习数量。
image.png
就我个人而言,我的做法是把全部单词刷一遍,过滤掉我完全掌握的,然后再进行新词的复习。

提醒

必须要给大家一个提醒,避免进入单词学习的陷阱:那就是忍受不了单词学习进度缓慢、记了又忘的情况,于是追求“超级记忆术”,把单词一股脑装在脑子里。

回顾前面所说,学习英语强调的是实践,而不是记忆。所以,不论记忆方法有多高明,它只能解决输入,不能解决输出的问题。因此,我们的重点应该放在输入与输出闭环上,而不是追求单方面“多快好省”。

网络平台上很多鼓吹类似于“一天背六千个单词”的营销内容,就是抓住了人们没有正确理解的情况下贪多求快的心理,一定要警惕。

再者,单词与阅读理解,并非简单的线性对应关系,即不是单词量学得越多,阅读理解越深。另一方面来说,也不是只有单词量“爆表”,才能进行全英文阅读。

以我自身为例,我可以阅读原著,但我的词汇量不过四千左右。

',12),m={href:"http://testyourvocab.com/result?user=13229458",target:"_blank",rel:"noopener noreferrer"},g=r("br",null,null,-1),_=r("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682426218162.png",alt:"image.png",loading:"lazy"},null,-1),u=i('

4360 是网站为我估算的单词数量。

为求精确,我还找了一本考研(包含了高考、四级、六级、考研词汇)的单词本,把上面的所有单词刷了一遍,结果如下:
image.png
上图的含义为:

  • 0 个未学习,意思是单词本的所有单词我都遇到了一遍了,并且我都进行了掌握程度判断
  • 已掌握 3844,意是这些单词我一看就能知道是什么意思,并且能做翻译
  • 学习中 4122,具体又可细分为:
    • 认识:一词多义,仅知道部分的中文含义,当其在另一个句子中体现另一种意思时,自己不知道
    • 模糊:原先不知道中文含义,但根据例句能隐约推断出其含义
    • 不认识:给了例句也看不懂

从上面我对学习中的单词的分类就可以看出,单词这件事,不是非黑即白的。而把单词掌握数量直接与英语能力挂钩,也是不可取的。

我在学生时代,其实没有买过一本单词本,也没有刻意地去背过单词(例如每天固定背50个),所以上述词汇量,还是挺客观的。就以词汇量而言,可能很多人都比我强——既然单词对我来说不是困扰,那对大家来说,应该也不是。我想这点信心,大家是可以有的。

总之,我想说的是,单词重要,却也不用刻意强调“背”这件事。把单词与英语能力直接挂钩,追求神奇的背单词的方法,这是很多人学英语的误区。而在这种误区下,就催生了速成的方法论,迎合了浮躁社会下急功近利的心态。但其实,强调实践,讲究输入输出闭环,才是更符合客观规律的学习方式。

记住:重要的不是你记住了多少单词,而是你能运用多少单词来造句。

',7);function f(b,y){const n=a("ExternalLinkIcon"),o=a("RouterLink");return s(),p("div",null,[h,r("p",null,[e("我之前在某个词汇量评测网站做过评估:"),r("a",m,[e("http://testyourvocab.com/result?user=13229458"),l(n)]),g,_]),u,r("p",null,[e("延伸阅读:"),l(o,{to:"/english/learning-7000-words-task-completed.html"},{default:c(()=>[e("刷7000个单词的经验总结")]),_:1}),e("。")])])}const k=t(d,[["render",f],["__file","everyone-can-learn-english-3-words.html.vue"]]);export{k as default}; +import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as a,o as s,c as p,a as r,b as e,d as l,w as c,f as i}from"./app-a9d55428.js";const d={},h=i('

人人都能学会的英语3:单词

方法

单词是听说读写的基础,是有必要花时间去学习的。

然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议:

  1. 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握
  2. 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。
  3. 只推荐利用词根记忆(当然并非百分百有效)
  4. 学习软件:欧路词典——免费,全平台通用

以手机欧路词典为例,打开后,点击“学习”,选择适合自己的单词本(或先做测试让软件来推荐),进行学习即可。
image.png
注意结合个人情况,进行学习设置,例如学习新词数量及、复习数量。
image.png
就我个人而言,我的做法是把全部单词刷一遍,过滤掉我完全掌握的,然后再进行新词的复习。

提醒

必须要给大家一个提醒,避免进入单词学习的陷阱:那就是忍受不了单词学习进度缓慢、记了又忘的情况,于是追求“超级记忆术”,把单词一股脑装在脑子里。

回顾前面所说,学习英语强调的是实践,而不是记忆。所以,不论记忆方法有多高明,它只能解决输入,不能解决输出的问题。因此,我们的重点应该放在输入与输出闭环上,而不是追求单方面“多快好省”。

网络平台上很多鼓吹类似于“一天背六千个单词”的营销内容,就是抓住了人们没有正确理解的情况下贪多求快的心理,一定要警惕。

再者,单词与阅读理解,并非简单的线性对应关系,即不是单词量学得越多,阅读理解越深。另一方面来说,也不是只有单词量“爆表”,才能进行全英文阅读。

以我自身为例,我可以阅读原著,但我的词汇量不过四千左右。

',12),m={href:"http://testyourvocab.com/result?user=13229458",target:"_blank",rel:"noopener noreferrer"},g=r("br",null,null,-1),_=r("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682426218162.png",alt:"image.png",loading:"lazy"},null,-1),u=i('

4360 是网站为我估算的单词数量。

为求精确,我还找了一本考研(包含了高考、四级、六级、考研词汇)的单词本,把上面的所有单词刷了一遍,结果如下:
image.png
上图的含义为:

  • 0 个未学习,意思是单词本的所有单词我都遇到了一遍了,并且我都进行了掌握程度判断
  • 已掌握 3844,意是这些单词我一看就能知道是什么意思,并且能做翻译
  • 学习中 4122,具体又可细分为:
    • 认识:一词多义,仅知道部分的中文含义,当其在另一个句子中体现另一种意思时,自己不知道
    • 模糊:原先不知道中文含义,但根据例句能隐约推断出其含义
    • 不认识:给了例句也看不懂

从上面我对学习中的单词的分类就可以看出,单词这件事,不是非黑即白的。而把单词掌握数量直接与英语能力挂钩,也是不可取的。

我在学生时代,其实没有买过一本单词本,也没有刻意地去背过单词(例如每天固定背50个),所以上述词汇量,还是挺客观的。就以词汇量而言,可能很多人都比我强——既然单词对我来说不是困扰,那对大家来说,应该也不是。我想这点信心,大家是可以有的。

总之,我想说的是,单词重要,却也不用刻意强调“背”这件事。把单词与英语能力直接挂钩,追求神奇的背单词的方法,这是很多人学英语的误区。而在这种误区下,就催生了速成的方法论,迎合了浮躁社会下急功近利的心态。但其实,强调实践,讲究输入输出闭环,才是更符合客观规律的学习方式。

记住:重要的不是你记住了多少单词,而是你能运用多少单词来造句。

',7);function f(b,y){const n=a("ExternalLinkIcon"),o=a("RouterLink");return s(),p("div",null,[h,r("p",null,[e("我之前在某个词汇量评测网站做过评估:"),r("a",m,[e("http://testyourvocab.com/result?user=13229458"),l(n)]),g,_]),u,r("p",null,[e("延伸阅读:"),l(o,{to:"/english/learning-7000-words-task-completed.html"},{default:c(()=>[e("刷7000个单词的经验总结")]),_:1}),e("。")])])}const k=t(d,[["render",f],["__file","everyone-can-learn-english-3-words.html.vue"]]);export{k as default}; diff --git a/assets/everyone-can-learn-english-4-listening-and-speaking.html-37f330d6.js b/assets/everyone-can-learn-english-4-listening-and-speaking.html-8bf4c725.js similarity index 98% rename from assets/everyone-can-learn-english-4-listening-and-speaking.html-37f330d6.js rename to assets/everyone-can-learn-english-4-listening-and-speaking.html-8bf4c725.js index ae4c76bc..fa028adc 100644 --- a/assets/everyone-can-learn-english-4-listening-and-speaking.html-37f330d6.js +++ b/assets/everyone-can-learn-english-4-listening-and-speaking.html-8bf4c725.js @@ -1 +1 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as i,c as a,a as e,b as n,d as s,f as o}from"./app-b649ee34.js";const h={},c=o('

人人都能学会的英语4:听说

前言

听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。

方法

听说属于综合能力,建议遵循以下学习步骤:

  1. 激发兴趣,建立信心
  2. 明确方向
  3. 脚踏实地,坚持输入并输出

1.建立信心

首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料:

',8),d={href:"https://b23.tv/61xkAgc",target:"_blank",rel:"noopener noreferrer"},p={href:"https://www.bbc.co.uk/learningenglish/english/features/english-at-work",target:"_blank",rel:"noopener noreferrer"},u=e("p",null,[n("前者是西游记英文动画版,后者是BBC的职场英语动画片,任选其一即可(如果是实用主义,当然选第二个啦)。"),e("br"),n(" 两个材料都是只有英文字幕的,要逐渐习惯这种模式。这就是为什么说学习时要注重心理建设,因为需要走出舒适区。")],-1),_=e("p",null,"记住,上面给的材料,主要是帮助自己建立自信的, 学到什么不重要,重要的是你觉得自己行了,可以进行下一步了。",-1),g=e("h3",{id:"_2-明确方向",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#_2-明确方向","aria-hidden":"true"},"#"),n(" 2.明确方向")],-1),f=e("p",null,"在进行深入学习前,我们需要明确自己的学习方向,究竟是 Daily English 还是 Business English?",-1),b=e("p",null,"当然可以全部都学,但那是结果,在过程中总要分个前后。对这个问题的回答,取决于读者自己。",-1),w={href:"https://www.youtube.com/c/EnglishSpeakingCourses/search?query=conversation",target:"_blank",rel:"noopener noreferrer"},k=e("br",null,null,-1),m={href:"https://www.yuque.com/levy/blog/how-to-surf",target:"_blank",rel:"noopener noreferrer"},x=e("p",null,"Business English,我推荐 eslpod 的教材:",-1),v={href:"https://secure3.eslpod.com/product/using-english-at-work/",target:"_blank",rel:"noopener noreferrer"},y={href:"https://secure3.eslpod.com/product/english-for-business-meetings/",target:"_blank",rel:"noopener noreferrer"},E={href:"https://secure3.eslpod.com/product/interview-questions-answered/",target:"_blank",rel:"noopener noreferrer"},B=o('

这里特别说下,eslpod 的创始人是英语方面的语言学及教育学的博士,可以说没有人比他更懂英语教学(手动滑稽)!跟着他学,我成功地喜欢上了美语,从此以后听加州口音会觉得最舒服。

以上三本教材我自己买了,有需要的可以私信我发你。

另外,赖世雄的《高级美语》也极力推荐,它是属于综合性的教材,内容方面起到开阔视野的作用。而我推荐它的原因是,本书北美外教的发音尤为悦耳,朗读课文时简直“说得比唱得还好听”,练习听说的同学一定不要错过。

3.坚持输入并输出

有了材料还远远不够,只是在听、光在那里看,结果还是哑巴英语。因此,对于无论什么材料,我建议这样使用:

  1. 不看课本(字幕),全部语音听完
  2. 看课本(字幕),做笔记,学习单词与短语
  3. 慢速跟读
    1. 看课本(字幕),播一句,暂停,跟读一句
    2. 不看课本(字幕),播一句,暂停,跟读一句
    3. 看课本(字幕),不暂停,课程跟读
  4. 独自念出来,并使用手机软件(如微信)录音,听一听自己说的效果,并进行语音翻译,以检测自己的输出正确率
  5. 脱稿表达(此步骤可选)

在这里分享一个我遇到的“困境”。在进行不看课本的慢速跟读的练习时,播一句跟读一句的形式,让我很容易犯困。但我没有因此就放弃,也没想办法强行提神,而是转而把它当作催眠工具——困了就睡觉。这样过了两周,有一天晚上,我没有犯困,做完了训练。而那一天也是我走出“困境”的日子,后面的日子里,再也没犯困过!

其实我不止有一次类似的“犯困”经历,都是与学习新东西有关的。我想,这可能是大脑在生理上对学习的抗拒。对于新领域的知识或不同寻常的学习方式,是需要走出舒适区的,而这种“犯困”可能是大脑潜意识在拒绝走出舒适区。对此,应对方法很简单,那就是坚持下去,每天固定时间执行相应的学习动作,直到大脑潜意识“屈服”,转而适应新的东西。

以上就是今天要分享的关于听说练习的内容,下一期将分享英语读写的学习经验。

',9);function q(C,A){const r=l("ExternalLinkIcon");return i(),a("div",null,[c,e("ul",null,[e("li",null,[n("Journey to the West: "),e("a",d,[n("https://b23.tv/61xkAgc"),s(r)])]),e("li",null,[n("English at Work:"),e("a",p,[n("https://www.bbc.co.uk/learningenglish/english/features/english-at-work"),s(r)])])]),u,_,g,f,b,e("p",null,[n("Daily English 我推荐:"),e("a",w,[n("https://www.youtube.com/c/EnglishSpeakingCourses/search?query=conversation"),s(r)]),k,n(" 这个视频如果看不了,可以看我写的教程:"),e("a",m,[n("https://www.yuque.com/levy/blog/how-to-surf"),s(r)])]),x,e("ol",null,[e("li",null,[e("a",v,[n("Using English at Work"),s(r)])]),e("li",null,[e("a",y,[n("English for Business Meetings"),s(r)])]),e("li",null,[e("a",E,[n("Interview Questions Answered"),s(r)])])]),B])}const S=t(h,[["render",q],["__file","everyone-can-learn-english-4-listening-and-speaking.html.vue"]]);export{S as default}; +import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as i,c as a,a as e,b as n,d as s,f as o}from"./app-a9d55428.js";const h={},c=o('

人人都能学会的英语4:听说

前言

听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。

方法

听说属于综合能力,建议遵循以下学习步骤:

  1. 激发兴趣,建立信心
  2. 明确方向
  3. 脚踏实地,坚持输入并输出

1.建立信心

首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料:

',8),d={href:"https://b23.tv/61xkAgc",target:"_blank",rel:"noopener noreferrer"},p={href:"https://www.bbc.co.uk/learningenglish/english/features/english-at-work",target:"_blank",rel:"noopener noreferrer"},u=e("p",null,[n("前者是西游记英文动画版,后者是BBC的职场英语动画片,任选其一即可(如果是实用主义,当然选第二个啦)。"),e("br"),n(" 两个材料都是只有英文字幕的,要逐渐习惯这种模式。这就是为什么说学习时要注重心理建设,因为需要走出舒适区。")],-1),_=e("p",null,"记住,上面给的材料,主要是帮助自己建立自信的, 学到什么不重要,重要的是你觉得自己行了,可以进行下一步了。",-1),g=e("h3",{id:"_2-明确方向",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#_2-明确方向","aria-hidden":"true"},"#"),n(" 2.明确方向")],-1),f=e("p",null,"在进行深入学习前,我们需要明确自己的学习方向,究竟是 Daily English 还是 Business English?",-1),b=e("p",null,"当然可以全部都学,但那是结果,在过程中总要分个前后。对这个问题的回答,取决于读者自己。",-1),w={href:"https://www.youtube.com/c/EnglishSpeakingCourses/search?query=conversation",target:"_blank",rel:"noopener noreferrer"},k=e("br",null,null,-1),m={href:"https://www.yuque.com/levy/blog/how-to-surf",target:"_blank",rel:"noopener noreferrer"},x=e("p",null,"Business English,我推荐 eslpod 的教材:",-1),v={href:"https://secure3.eslpod.com/product/using-english-at-work/",target:"_blank",rel:"noopener noreferrer"},y={href:"https://secure3.eslpod.com/product/english-for-business-meetings/",target:"_blank",rel:"noopener noreferrer"},E={href:"https://secure3.eslpod.com/product/interview-questions-answered/",target:"_blank",rel:"noopener noreferrer"},B=o('

这里特别说下,eslpod 的创始人是英语方面的语言学及教育学的博士,可以说没有人比他更懂英语教学(手动滑稽)!跟着他学,我成功地喜欢上了美语,从此以后听加州口音会觉得最舒服。

以上三本教材我自己买了,有需要的可以私信我发你。

另外,赖世雄的《高级美语》也极力推荐,它是属于综合性的教材,内容方面起到开阔视野的作用。而我推荐它的原因是,本书北美外教的发音尤为悦耳,朗读课文时简直“说得比唱得还好听”,练习听说的同学一定不要错过。

3.坚持输入并输出

有了材料还远远不够,只是在听、光在那里看,结果还是哑巴英语。因此,对于无论什么材料,我建议这样使用:

  1. 不看课本(字幕),全部语音听完
  2. 看课本(字幕),做笔记,学习单词与短语
  3. 慢速跟读
    1. 看课本(字幕),播一句,暂停,跟读一句
    2. 不看课本(字幕),播一句,暂停,跟读一句
    3. 看课本(字幕),不暂停,课程跟读
  4. 独自念出来,并使用手机软件(如微信)录音,听一听自己说的效果,并进行语音翻译,以检测自己的输出正确率
  5. 脱稿表达(此步骤可选)

在这里分享一个我遇到的“困境”。在进行不看课本的慢速跟读的练习时,播一句跟读一句的形式,让我很容易犯困。但我没有因此就放弃,也没想办法强行提神,而是转而把它当作催眠工具——困了就睡觉。这样过了两周,有一天晚上,我没有犯困,做完了训练。而那一天也是我走出“困境”的日子,后面的日子里,再也没犯困过!

其实我不止有一次类似的“犯困”经历,都是与学习新东西有关的。我想,这可能是大脑在生理上对学习的抗拒。对于新领域的知识或不同寻常的学习方式,是需要走出舒适区的,而这种“犯困”可能是大脑潜意识在拒绝走出舒适区。对此,应对方法很简单,那就是坚持下去,每天固定时间执行相应的学习动作,直到大脑潜意识“屈服”,转而适应新的东西。

以上就是今天要分享的关于听说练习的内容,下一期将分享英语读写的学习经验。

',9);function q(C,A){const r=l("ExternalLinkIcon");return i(),a("div",null,[c,e("ul",null,[e("li",null,[n("Journey to the West: "),e("a",d,[n("https://b23.tv/61xkAgc"),s(r)])]),e("li",null,[n("English at Work:"),e("a",p,[n("https://www.bbc.co.uk/learningenglish/english/features/english-at-work"),s(r)])])]),u,_,g,f,b,e("p",null,[n("Daily English 我推荐:"),e("a",w,[n("https://www.youtube.com/c/EnglishSpeakingCourses/search?query=conversation"),s(r)]),k,n(" 这个视频如果看不了,可以看我写的教程:"),e("a",m,[n("https://www.yuque.com/levy/blog/how-to-surf"),s(r)])]),x,e("ol",null,[e("li",null,[e("a",v,[n("Using English at Work"),s(r)])]),e("li",null,[e("a",y,[n("English for Business Meetings"),s(r)])]),e("li",null,[e("a",E,[n("Interview Questions Answered"),s(r)])])]),B])}const S=t(h,[["render",q],["__file","everyone-can-learn-english-4-listening-and-speaking.html.vue"]]);export{S as default}; diff --git a/assets/everyone-can-learn-english-4-listening-and-speaking.html-e5671ccf.js b/assets/everyone-can-learn-english-4-listening-and-speaking.html-bcf756a7.js similarity index 94% rename from assets/everyone-can-learn-english-4-listening-and-speaking.html-e5671ccf.js rename to assets/everyone-can-learn-english-4-listening-and-speaking.html-bcf756a7.js index 31ec023a..0dc8b9a8 100644 --- a/assets/everyone-can-learn-english-4-listening-and-speaking.html-e5671ccf.js +++ b/assets/everyone-can-learn-english-4-listening-and-speaking.html-bcf756a7.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-788d194a","path":"/english/everyone-can-learn-english-4-listening-and-speaking.html","title":"人人都能学会的英语4:听说","lang":"zh-CN","frontmatter":{"date":"2022-06-28T00:00:00.000Z","tag":"English","description":"人人都能学会的英语4:听说 前言 听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。 方法 听说属于综合能力,建议遵循以下学习步骤: 激发兴趣,建立信心 明确方向 脚踏实地,坚持输入并输出 1.建立信心 首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料: Journey to the West: https://b23.tv/61xkAgc English at Work:https://www.bbc.co.uk/learningenglish/english/features/english-at-work","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-4-listening-and-speaking.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语4:听说"}],["meta",{"property":"og:description","content":"人人都能学会的英语4:听说 前言 听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。 方法 听说属于综合能力,建议遵循以下学习步骤: 激发兴趣,建立信心 明确方向 脚踏实地,坚持输入并输出 1.建立信心 首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料: Journey to the West: https://b23.tv/61xkAgc English at Work:https://www.bbc.co.uk/learningenglish/english/features/english-at-work"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-28T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语4:听说\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-28T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"方法","slug":"方法","link":"#方法","children":[{"level":3,"title":"1.建立信心","slug":"_1-建立信心","link":"#_1-建立信心","children":[]},{"level":3,"title":"2.明确方向","slug":"_2-明确方向","link":"#_2-明确方向","children":[]},{"level":3,"title":"3.坚持输入并输出","slug":"_3-坚持输入并输出","link":"#_3-坚持输入并输出","children":[]}]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.82,"words":1146},"filePathRelative":"english/everyone-can-learn-english-4-listening-and-speaking.md","localizedDate":"2022年6月28日","excerpt":"

人人都能学会的英语4:听说

\\n

前言

\\n

听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。

\\n

方法

\\n

听说属于综合能力,建议遵循以下学习步骤:

\\n
    \\n
  1. 激发兴趣,建立信心
  2. \\n
  3. 明确方向
  4. \\n
  5. 脚踏实地,坚持输入并输出
  6. \\n
\\n

1.建立信心

\\n

首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料:

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-788d194a","path":"/english/everyone-can-learn-english-4-listening-and-speaking.html","title":"人人都能学会的英语4:听说","lang":"zh-CN","frontmatter":{"date":"2022-06-28T00:00:00.000Z","tag":"English","description":"人人都能学会的英语4:听说 前言 听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。 方法 听说属于综合能力,建议遵循以下学习步骤: 激发兴趣,建立信心 明确方向 脚踏实地,坚持输入并输出 1.建立信心 首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料: Journey to the West: https://b23.tv/61xkAgc English at Work:https://www.bbc.co.uk/learningenglish/english/features/english-at-work","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-4-listening-and-speaking.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语4:听说"}],["meta",{"property":"og:description","content":"人人都能学会的英语4:听说 前言 听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。 方法 听说属于综合能力,建议遵循以下学习步骤: 激发兴趣,建立信心 明确方向 脚踏实地,坚持输入并输出 1.建立信心 首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料: Journey to the West: https://b23.tv/61xkAgc English at Work:https://www.bbc.co.uk/learningenglish/english/features/english-at-work"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-28T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语4:听说\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-28T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"方法","slug":"方法","link":"#方法","children":[{"level":3,"title":"1.建立信心","slug":"_1-建立信心","link":"#_1-建立信心","children":[]},{"level":3,"title":"2.明确方向","slug":"_2-明确方向","link":"#_2-明确方向","children":[]},{"level":3,"title":"3.坚持输入并输出","slug":"_3-坚持输入并输出","link":"#_3-坚持输入并输出","children":[]}]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.82,"words":1146},"filePathRelative":"english/everyone-can-learn-english-4-listening-and-speaking.md","localizedDate":"2022年6月28日","excerpt":"

人人都能学会的英语4:听说

\\n

前言

\\n

听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。

\\n

方法

\\n

听说属于综合能力,建议遵循以下学习步骤:

\\n
    \\n
  1. 激发兴趣,建立信心
  2. \\n
  3. 明确方向
  4. \\n
  5. 脚踏实地,坚持输入并输出
  6. \\n
\\n

1.建立信心

\\n

首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料:

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/everyone-can-learn-english-5-reading-and-writing.html-3815eb16.js b/assets/everyone-can-learn-english-5-reading-and-writing.html-630438e5.js similarity index 95% rename from assets/everyone-can-learn-english-5-reading-and-writing.html-3815eb16.js rename to assets/everyone-can-learn-english-5-reading-and-writing.html-630438e5.js index 3bd6d2e8..574b5231 100644 --- a/assets/everyone-can-learn-english-5-reading-and-writing.html-3815eb16.js +++ b/assets/everyone-can-learn-english-5-reading-and-writing.html-630438e5.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-ae153a4e","path":"/english/everyone-can-learn-english-5-reading-and-writing.html","title":"人人都能学会的英语5:读写","lang":"zh-CN","frontmatter":{"date":"2022-06-29T00:00:00.000Z","tag":"English","description":"人人都能学会的英语5:读写 前言 读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。 阅读能力升级之旅 我先给出自己经历过的全英阅读能力的变化过程,仅供参考: 看到英文网站,第一反应是点击切换中文版 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域) 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-5-reading-and-writing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语5:读写"}],["meta",{"property":"og:description","content":"人人都能学会的英语5:读写 前言 读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。 阅读能力升级之旅 我先给出自己经历过的全英阅读能力的变化过程,仅供参考: 看到英文网站,第一反应是点击切换中文版 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域) 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-29T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语5:读写\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-29T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"阅读能力升级之旅","slug":"阅读能力升级之旅","link":"#阅读能力升级之旅","children":[]},{"level":2,"title":"阅读方法","slug":"阅读方法","link":"#阅读方法","children":[{"level":3,"title":"建立信心","slug":"建立信心","link":"#建立信心","children":[]},{"level":3,"title":"根据蓝思值选书","slug":"根据蓝思值选书","link":"#根据蓝思值选书","children":[]},{"level":3,"title":"巧查生词","slug":"巧查生词","link":"#巧查生词","children":[]},{"level":3,"title":"阅读材料推荐","slug":"阅读材料推荐","link":"#阅读材料推荐","children":[]}]},{"level":2,"title":"写作","slug":"写作","link":"#写作","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":6.15,"words":1846},"filePathRelative":"english/everyone-can-learn-english-5-reading-and-writing.md","localizedDate":"2022年6月29日","excerpt":"

人人都能学会的英语5:读写

\\n

前言

\\n

读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。

\\n

阅读能力升级之旅

\\n

我先给出自己经历过的全英阅读能力的变化过程,仅供参考:

\\n
    \\n
  1. 看到英文网站,第一反应是点击切换中文版
  2. \\n
  3. 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读
  4. \\n
  5. 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域)
  6. \\n
  7. 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读
  8. \\n
  9. 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读
  10. \\n
  11. 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了
  12. \\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-ae153a4e","path":"/english/everyone-can-learn-english-5-reading-and-writing.html","title":"人人都能学会的英语5:读写","lang":"zh-CN","frontmatter":{"date":"2022-06-29T00:00:00.000Z","tag":"English","description":"人人都能学会的英语5:读写 前言 读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。 阅读能力升级之旅 我先给出自己经历过的全英阅读能力的变化过程,仅供参考: 看到英文网站,第一反应是点击切换中文版 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域) 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-5-reading-and-writing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语5:读写"}],["meta",{"property":"og:description","content":"人人都能学会的英语5:读写 前言 读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。 阅读能力升级之旅 我先给出自己经历过的全英阅读能力的变化过程,仅供参考: 看到英文网站,第一反应是点击切换中文版 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域) 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-29T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语5:读写\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-29T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"阅读能力升级之旅","slug":"阅读能力升级之旅","link":"#阅读能力升级之旅","children":[]},{"level":2,"title":"阅读方法","slug":"阅读方法","link":"#阅读方法","children":[{"level":3,"title":"建立信心","slug":"建立信心","link":"#建立信心","children":[]},{"level":3,"title":"根据蓝思值选书","slug":"根据蓝思值选书","link":"#根据蓝思值选书","children":[]},{"level":3,"title":"巧查生词","slug":"巧查生词","link":"#巧查生词","children":[]},{"level":3,"title":"阅读材料推荐","slug":"阅读材料推荐","link":"#阅读材料推荐","children":[]}]},{"level":2,"title":"写作","slug":"写作","link":"#写作","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":6.15,"words":1846},"filePathRelative":"english/everyone-can-learn-english-5-reading-and-writing.md","localizedDate":"2022年6月29日","excerpt":"

人人都能学会的英语5:读写

\\n

前言

\\n

读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。

\\n

阅读能力升级之旅

\\n

我先给出自己经历过的全英阅读能力的变化过程,仅供参考:

\\n
    \\n
  1. 看到英文网站,第一反应是点击切换中文版
  2. \\n
  3. 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读
  4. \\n
  5. 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域)
  6. \\n
  7. 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读
  8. \\n
  9. 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读
  10. \\n
  11. 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了
  12. \\n
","autoDesc":true}');export{e as data}; diff --git a/assets/everyone-can-learn-english-5-reading-and-writing.html-63730d51.js b/assets/everyone-can-learn-english-5-reading-and-writing.html-cf6b1e9b.js similarity index 99% rename from assets/everyone-can-learn-english-5-reading-and-writing.html-63730d51.js rename to assets/everyone-can-learn-english-5-reading-and-writing.html-cf6b1e9b.js index 5bf053de..f679d95a 100644 --- a/assets/everyone-can-learn-english-5-reading-and-writing.html-63730d51.js +++ b/assets/everyone-can-learn-english-5-reading-and-writing.html-cf6b1e9b.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as s,c as h,a as e,b as i,d as n,w as d,f as r}from"./app-b649ee34.js";const p={},c=r('

人人都能学会的英语5:读写

前言

读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。

阅读能力升级之旅

我先给出自己经历过的全英阅读能力的变化过程,仅供参考:

  1. 看到英文网站,第一反应是点击切换中文版
  2. 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读
  3. 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域)
  4. 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读
  5. 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读
  6. 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了

这是怎么做到的呢?我主要采用了以下方法。

阅读方法

建立信心

如果之前读者并没有全英文的阅读体验,建议先根据兴趣选一本书(或阅读材料)后,在两周内读完它——无论用什么手段,一定要在该时间段内完成。此举似小实大,有两点内涵:

  • 时长再拉长,你很可能读了后面忘前面;时间越长,越拖延,则两相结合,很可能最终放弃了阅读
  • 在较短的时间内读完,能快速建立信心,为后续阅读打下基础

第一本书或阅读材料很关键,感兴趣是最重要的,当然也可以结合后面提到的蓝思值进行选材。

我当初使用的是 轻听英语 app,在上面看完了英文文字加语音版的死亡笔记。看完后感受如下:

  • 一开始的阅读速度比音频的播放速度还要慢,如果没有兴趣,很可能就没耐心了
  • 因为是动漫的文字版,除了画外音(内心的独白),几乎全是人物的对话,这些内容是很容易理解的

之后我信心大涨,用一周时间,把魔兽世界官方小说《Arthas: Rise of the Lich King》原版看完了。
image.png

根据蓝思值选书

有了信心之后,我们就可以科学地、有节奏地培养自己的英文阅读能力了。

',17),g={href:"https://lexile.com/parents-students/understanding-your-lexile-measure/lexile-measures-reading/",target:"_blank",rel:"noopener noreferrer"},_=e("br",null,null,-1),u=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427484594.png",alt:"image.png",loading:"lazy"},null,-1),m=e("br",null,null,-1),b=e("br",null,null,-1),f=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427489979.png",alt:"image.png",loading:"lazy"},null,-1),y=e("p",null,"综上所述,推荐按以下步骤提升阅读能力:",-1),x={href:"https://readtheory.org/",target:"_blank",rel:"noopener noreferrer"},k=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427496404.png",alt:"image.png",loading:"lazy"},null,-1),w={href:"https://hub.lexile.com/find-a-book/book-results",target:"_blank",rel:"noopener noreferrer"},v=e("br",null,null,-1),z={href:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427501518.png",target:"_blank",rel:"noopener noreferrer"},L={href:"https://cdn.nlark.com/yuque/0/2022/jpeg/160590/1656413042641-47c45b34-b4d2-4197-9af8-536ea2e8b4be.jpeg#averageHue=%23f0edeb&clientId=u576bfa2c-89a1-4&from=paste&height=471&id=u89b97437&name=lADPJv8gUK04WVbNBQDNAz4_830_1280.jpg&originHeight=1280&originWidth=830&originalType=binary&ratio=1&rotation=0&showTitle=false&size=204047&status=done&style=none&taskId=udfd982fe-6ea1-4219-89f5-d3a68b12c42&title=&width=305.3333740234375",target:"_blank",rel:"noopener noreferrer"},N=r('

巧查生词

阅读的一个拦路虎是生词。大家的第一反应是认为生词太多,看不懂,很容易放弃。

首先,我推荐欧路词典。如果是在电脑上看 pdf,鼠标选中区域就能查词或翻译,非常方便。

再者,表达一个观点:有些单词不认识,甚至有些句子无法理解,并不影响对情节的核心内容的理解。事实上,除去欧亨利式结尾的短篇小说,书的内容越多,其核心内容越不容易受生词影响。因为主题是贯穿全文的,前面因为生词没看懂,后文换了个说法,你就能看得懂了!

因此,对生词,我建议:一页只查一个生词。它的意义是:

  1. 你不会因为频繁查生词而影响阅读体验。事实上,试图把每一个生词都查一遍,是阅读不下去的主要原因!
  2. 因为查词机会的“稀缺性”,你把机会留给你认为最有价值的生词
  3. 强迫你的大脑思考,如何在带着许多“迷雾”的情况下,去理解句子、段落,抓住故事的核心情节,理解书籍的中心思想

在阅读《Arthas: Rise of the Lich King》的时候,我的体验就是:

  • 有些形容词或副词不认识,并不影响理解句子的大意
  • 阅读几章后,为加快阅读速度,只出现一次的生词,我不再查字典——我只查出现过至少两次的单词
  • 再看到后面,有些单词不认识,我都懒得查了,反正不影响我对整体剧情的理解

阅读材料推荐

',9),B=e("h2",{id:"写作",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#写作","aria-hidden":"true"},"#"),i(" 写作")],-1),A=e("p",null,"阅读是输入,写作是输出,二者是相辅相成的。 对于写的能力,推荐从读书笔记开始做练习 (就像看完中文书那样做笔记),也可以找机会与人发邮件。",-1),W=e("p",null,"读书笔记示例:",-1),j=e("figure",null,[e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/indispensable-opposition.png",alt:"indispensable-opposition.png",tabindex:"0",loading:"lazy"}),e("figcaption",null,"indispensable-opposition.png")],-1),V={href:"http://eslpod.com",target:"_blank",rel:"noopener noreferrer"},D=e("br",null,null,-1),I=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427508571.png",alt:"image.png",loading:"lazy"},null,-1),R=e("p",null,[i("这是我申请 JetBrains 正版授权时发的邮件:"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427519794.png",alt:"image.png",loading:"lazy"})],-1),T=e("p",null,[i("还有一种方法, 那就是利用软件的翻译功能,把自己写的中文笔记,翻译成英文,再去校对、修改。很多文本类 Web 应用都有此功能,可以利用起来。"),e("br"),i(" 这种方法本质就是,自己一开始不知道英文文章写什么,那就从校对、修改开始做起;自己不能写完整的英文文章,那就从摘取段落开始做起。适应这个过程后,写作的障碍就减少了。")],-1),E=e("p",null,"总结一下,英文读写的提升需要踏出舒适区,要耐着性子,不要怕慢。坚持去做,量变终会有质变!",-1);function H(J,K){const a=l("ExternalLinkIcon"),t=l("RouterLink");return s(),h("div",null,[c,e("p",null,[i("核心思路是评估自己的阅读"),e("a",g,[i("蓝思值"),n(a)]),i(",再找到适合自己的阅读材料。什么是蓝思值呢?"),_,u,m,i(" 假设你的阅读水平是 600L,那么蓝思值在 550L~700L 的阅读材料比较适合你。再低就太简单,达不到提升的效果;再高就超出阅读能力太多,很可能读不下去(因为我们在讲全英文阅读,所以我就不翻译了)。"),b,f]),y,e("ol",null,[e("li",null,[i("评估自己的阅读能力 "),e("a",x,[i("https://readtheory.org/"),n(a)]),i(),k]),e("li",null,[i("找到自己感兴趣的读物,通过网站判定书籍的蓝思值是否合适 "),e("a",w,[i("https://hub.lexile.com/find-a-book/book-results"),n(a)])])]),e("p",null,[i("相关的 app 我推荐: 轻听英语,里面的书都是免费的。当然大家也可以推荐别的 app,直接有标明蓝思值的就最好不过了。"),v,i(" ![lADPJw1WS0BSWVnNBQDNBMw_1228_1280.jpg]("),e("a",z,[i("https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427501518.png"),n(a)]),i("]("),e("a",L,[i("https://cdn.nlark.com/yuque/0/2022/jpeg/160590/1656413042641-47c45b34-b4d2-4197-9af8-536ea2e8b4be.jpeg#averageHue=%23f0edeb&clientId=u576bfa2c-89a1-4&from=paste&height=471&id=u89b97437&name=lADPJv8gUK04WVbNBQDNAz4_830_1280.jpg&originHeight=1280&originWidth=830&originalType=binary&ratio=1&rotation=0&showTitle=false&size=204047&status=done&style=none&taskId=udfd982fe-6ea1-4219-89f5-d3a68b12c42&title=&width=305.3333740234375"),n(a)]),i(")")]),N,e("p",null,[i("如果实在找不到全英阅读材料,我推荐看《现代大学英语精读(第2版)》, 我已经挑选了值得一读的文章,英语高考有120分或过了四级的人,应该可以直接"),n(t,{to:"/english/contemporary-college-english-1.html"},{default:d(()=>[i("点击查看")]),_:1}),i("。")]),B,A,W,j,e("p",null,[i("这是我购买 "),e("a",V,[i("eslpod.com"),n(a)]),i(" 教材时发的邮件:"),D,I]),R,T,E])}const Q=o(p,[["render",H],["__file","everyone-can-learn-english-5-reading-and-writing.html.vue"]]);export{Q as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as s,c as h,a as e,b as i,d as n,w as d,f as r}from"./app-a9d55428.js";const p={},c=r('

人人都能学会的英语5:读写

前言

读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。

阅读能力升级之旅

我先给出自己经历过的全英阅读能力的变化过程,仅供参考:

  1. 看到英文网站,第一反应是点击切换中文版
  2. 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读
  3. 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域)
  4. 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读
  5. 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读
  6. 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了

这是怎么做到的呢?我主要采用了以下方法。

阅读方法

建立信心

如果之前读者并没有全英文的阅读体验,建议先根据兴趣选一本书(或阅读材料)后,在两周内读完它——无论用什么手段,一定要在该时间段内完成。此举似小实大,有两点内涵:

  • 时长再拉长,你很可能读了后面忘前面;时间越长,越拖延,则两相结合,很可能最终放弃了阅读
  • 在较短的时间内读完,能快速建立信心,为后续阅读打下基础

第一本书或阅读材料很关键,感兴趣是最重要的,当然也可以结合后面提到的蓝思值进行选材。

我当初使用的是 轻听英语 app,在上面看完了英文文字加语音版的死亡笔记。看完后感受如下:

  • 一开始的阅读速度比音频的播放速度还要慢,如果没有兴趣,很可能就没耐心了
  • 因为是动漫的文字版,除了画外音(内心的独白),几乎全是人物的对话,这些内容是很容易理解的

之后我信心大涨,用一周时间,把魔兽世界官方小说《Arthas: Rise of the Lich King》原版看完了。
image.png

根据蓝思值选书

有了信心之后,我们就可以科学地、有节奏地培养自己的英文阅读能力了。

',17),g={href:"https://lexile.com/parents-students/understanding-your-lexile-measure/lexile-measures-reading/",target:"_blank",rel:"noopener noreferrer"},_=e("br",null,null,-1),u=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427484594.png",alt:"image.png",loading:"lazy"},null,-1),m=e("br",null,null,-1),b=e("br",null,null,-1),f=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427489979.png",alt:"image.png",loading:"lazy"},null,-1),y=e("p",null,"综上所述,推荐按以下步骤提升阅读能力:",-1),x={href:"https://readtheory.org/",target:"_blank",rel:"noopener noreferrer"},k=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427496404.png",alt:"image.png",loading:"lazy"},null,-1),w={href:"https://hub.lexile.com/find-a-book/book-results",target:"_blank",rel:"noopener noreferrer"},v=e("br",null,null,-1),z={href:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427501518.png",target:"_blank",rel:"noopener noreferrer"},L={href:"https://cdn.nlark.com/yuque/0/2022/jpeg/160590/1656413042641-47c45b34-b4d2-4197-9af8-536ea2e8b4be.jpeg#averageHue=%23f0edeb&clientId=u576bfa2c-89a1-4&from=paste&height=471&id=u89b97437&name=lADPJv8gUK04WVbNBQDNAz4_830_1280.jpg&originHeight=1280&originWidth=830&originalType=binary&ratio=1&rotation=0&showTitle=false&size=204047&status=done&style=none&taskId=udfd982fe-6ea1-4219-89f5-d3a68b12c42&title=&width=305.3333740234375",target:"_blank",rel:"noopener noreferrer"},N=r('

巧查生词

阅读的一个拦路虎是生词。大家的第一反应是认为生词太多,看不懂,很容易放弃。

首先,我推荐欧路词典。如果是在电脑上看 pdf,鼠标选中区域就能查词或翻译,非常方便。

再者,表达一个观点:有些单词不认识,甚至有些句子无法理解,并不影响对情节的核心内容的理解。事实上,除去欧亨利式结尾的短篇小说,书的内容越多,其核心内容越不容易受生词影响。因为主题是贯穿全文的,前面因为生词没看懂,后文换了个说法,你就能看得懂了!

因此,对生词,我建议:一页只查一个生词。它的意义是:

  1. 你不会因为频繁查生词而影响阅读体验。事实上,试图把每一个生词都查一遍,是阅读不下去的主要原因!
  2. 因为查词机会的“稀缺性”,你把机会留给你认为最有价值的生词
  3. 强迫你的大脑思考,如何在带着许多“迷雾”的情况下,去理解句子、段落,抓住故事的核心情节,理解书籍的中心思想

在阅读《Arthas: Rise of the Lich King》的时候,我的体验就是:

  • 有些形容词或副词不认识,并不影响理解句子的大意
  • 阅读几章后,为加快阅读速度,只出现一次的生词,我不再查字典——我只查出现过至少两次的单词
  • 再看到后面,有些单词不认识,我都懒得查了,反正不影响我对整体剧情的理解

阅读材料推荐

',9),B=e("h2",{id:"写作",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#写作","aria-hidden":"true"},"#"),i(" 写作")],-1),A=e("p",null,"阅读是输入,写作是输出,二者是相辅相成的。 对于写的能力,推荐从读书笔记开始做练习 (就像看完中文书那样做笔记),也可以找机会与人发邮件。",-1),W=e("p",null,"读书笔记示例:",-1),j=e("figure",null,[e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/indispensable-opposition.png",alt:"indispensable-opposition.png",tabindex:"0",loading:"lazy"}),e("figcaption",null,"indispensable-opposition.png")],-1),V={href:"http://eslpod.com",target:"_blank",rel:"noopener noreferrer"},D=e("br",null,null,-1),I=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427508571.png",alt:"image.png",loading:"lazy"},null,-1),R=e("p",null,[i("这是我申请 JetBrains 正版授权时发的邮件:"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427519794.png",alt:"image.png",loading:"lazy"})],-1),T=e("p",null,[i("还有一种方法, 那就是利用软件的翻译功能,把自己写的中文笔记,翻译成英文,再去校对、修改。很多文本类 Web 应用都有此功能,可以利用起来。"),e("br"),i(" 这种方法本质就是,自己一开始不知道英文文章写什么,那就从校对、修改开始做起;自己不能写完整的英文文章,那就从摘取段落开始做起。适应这个过程后,写作的障碍就减少了。")],-1),E=e("p",null,"总结一下,英文读写的提升需要踏出舒适区,要耐着性子,不要怕慢。坚持去做,量变终会有质变!",-1);function H(J,K){const a=l("ExternalLinkIcon"),t=l("RouterLink");return s(),h("div",null,[c,e("p",null,[i("核心思路是评估自己的阅读"),e("a",g,[i("蓝思值"),n(a)]),i(",再找到适合自己的阅读材料。什么是蓝思值呢?"),_,u,m,i(" 假设你的阅读水平是 600L,那么蓝思值在 550L~700L 的阅读材料比较适合你。再低就太简单,达不到提升的效果;再高就超出阅读能力太多,很可能读不下去(因为我们在讲全英文阅读,所以我就不翻译了)。"),b,f]),y,e("ol",null,[e("li",null,[i("评估自己的阅读能力 "),e("a",x,[i("https://readtheory.org/"),n(a)]),i(),k]),e("li",null,[i("找到自己感兴趣的读物,通过网站判定书籍的蓝思值是否合适 "),e("a",w,[i("https://hub.lexile.com/find-a-book/book-results"),n(a)])])]),e("p",null,[i("相关的 app 我推荐: 轻听英语,里面的书都是免费的。当然大家也可以推荐别的 app,直接有标明蓝思值的就最好不过了。"),v,i(" ![lADPJw1WS0BSWVnNBQDNBMw_1228_1280.jpg]("),e("a",z,[i("https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427501518.png"),n(a)]),i("]("),e("a",L,[i("https://cdn.nlark.com/yuque/0/2022/jpeg/160590/1656413042641-47c45b34-b4d2-4197-9af8-536ea2e8b4be.jpeg#averageHue=%23f0edeb&clientId=u576bfa2c-89a1-4&from=paste&height=471&id=u89b97437&name=lADPJv8gUK04WVbNBQDNAz4_830_1280.jpg&originHeight=1280&originWidth=830&originalType=binary&ratio=1&rotation=0&showTitle=false&size=204047&status=done&style=none&taskId=udfd982fe-6ea1-4219-89f5-d3a68b12c42&title=&width=305.3333740234375"),n(a)]),i(")")]),N,e("p",null,[i("如果实在找不到全英阅读材料,我推荐看《现代大学英语精读(第2版)》, 我已经挑选了值得一读的文章,英语高考有120分或过了四级的人,应该可以直接"),n(t,{to:"/english/contemporary-college-english-1.html"},{default:d(()=>[i("点击查看")]),_:1}),i("。")]),B,A,W,j,e("p",null,[i("这是我购买 "),e("a",V,[i("eslpod.com"),n(a)]),i(" 教材时发的邮件:"),D,I]),R,T,E])}const Q=o(p,[["render",H],["__file","everyone-can-learn-english-5-reading-and-writing.html.vue"]]);export{Q as default}; diff --git a/assets/export-mysql-table-into-excel.html-9bc68678.js b/assets/export-mysql-table-into-excel.html-3d06cd3e.js similarity index 89% rename from assets/export-mysql-table-into-excel.html-9bc68678.js rename to assets/export-mysql-table-into-excel.html-3d06cd3e.js index 437ad409..ff5674dc 100644 --- a/assets/export-mysql-table-into-excel.html-9bc68678.js +++ b/assets/export-mysql-table-into-excel.html-3d06cd3e.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-5b37b3c6","path":"/python/export-mysql-table-into-excel.html","title":"Python 导出 MySQL 库表信息到 Excel","lang":"zh-CN","frontmatter":{"date":"2023-03-05T00:00:00.000Z","tag":"Python","description":"Python 导出 MySQL 库表信息到 Excel 需求 查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。","head":[["meta",{"property":"og:url","content":"https://levy.vip/python/export-mysql-table-into-excel.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Python 导出 MySQL 库表信息到 Excel"}],["meta",{"property":"og:description","content":"Python 导出 MySQL 库表信息到 Excel 需求 查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:published_time","content":"2023-03-05T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Python 导出 MySQL 库表信息到 Excel\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-03-05T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"需求","slug":"需求","link":"#需求","children":[]},{"level":2,"title":"代码","slug":"代码","link":"#代码","children":[]},{"level":2,"title":"其他细节","slug":"其他细节","link":"#其他细节","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.47,"words":440},"filePathRelative":"python/export-mysql-table-into-excel.md","localizedDate":"2023年3月5日","excerpt":"

Python 导出 MySQL 库表信息到 Excel

\\n

需求

\\n

查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-5b37b3c6","path":"/python/export-mysql-table-into-excel.html","title":"Python 导出 MySQL 库表信息到 Excel","lang":"zh-CN","frontmatter":{"date":"2023-03-05T00:00:00.000Z","tag":"Python","description":"Python 导出 MySQL 库表信息到 Excel 需求 查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。","head":[["meta",{"property":"og:url","content":"https://levy.vip/python/export-mysql-table-into-excel.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Python 导出 MySQL 库表信息到 Excel"}],["meta",{"property":"og:description","content":"Python 导出 MySQL 库表信息到 Excel 需求 查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:published_time","content":"2023-03-05T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Python 导出 MySQL 库表信息到 Excel\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-03-05T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"需求","slug":"需求","link":"#需求","children":[]},{"level":2,"title":"代码","slug":"代码","link":"#代码","children":[]},{"level":2,"title":"其他细节","slug":"其他细节","link":"#其他细节","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.47,"words":440},"filePathRelative":"python/export-mysql-table-into-excel.md","localizedDate":"2023年3月5日","excerpt":"

Python 导出 MySQL 库表信息到 Excel

\\n

需求

\\n

查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/export-mysql-table-into-excel.html-6db61b30.js b/assets/export-mysql-table-into-excel.html-e4373920.js similarity index 99% rename from assets/export-mysql-table-into-excel.html-6db61b30.js rename to assets/export-mysql-table-into-excel.html-e4373920.js index a30e4bd6..ee345070 100644 --- a/assets/export-mysql-table-into-excel.html-6db61b30.js +++ b/assets/export-mysql-table-into-excel.html-e4373920.js @@ -1,4 +1,4 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as e,o as p,c as o,e as c,a as n,b as s,d as l,f as i}from"./app-b649ee34.js";const u={},r=n("h1",{id:"python-导出-mysql-库表信息到-excel",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#python-导出-mysql-库表信息到-excel","aria-hidden":"true"},"#"),s(" Python 导出 MySQL 库表信息到 Excel")],-1),k=n("h2",{id:"需求",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#需求","aria-hidden":"true"},"#"),s(" 需求")],-1),d=n("p",null,"查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。",-1),m=i(`

代码

import mysql.connector
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as e,o as p,c as o,e as c,a as n,b as s,d as l,f as i}from"./app-a9d55428.js";const u={},r=n("h1",{id:"python-导出-mysql-库表信息到-excel",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#python-导出-mysql-库表信息到-excel","aria-hidden":"true"},"#"),s(" Python 导出 MySQL 库表信息到 Excel")],-1),k=n("h2",{id:"需求",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#需求","aria-hidden":"true"},"#"),s(" 需求")],-1),d=n("p",null,"查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。",-1),m=i(`

代码

import mysql.connector
 import xlsxwriter
 
 # 根据实际修改下面的变量
diff --git a/assets/git-best-pratices.html-73d0401b.js b/assets/git-best-pratices.html-4b6532e6.js
similarity index 98%
rename from assets/git-best-pratices.html-73d0401b.js
rename to assets/git-best-pratices.html-4b6532e6.js
index 2cc274f7..8aaa7c2c 100644
--- a/assets/git-best-pratices.html-73d0401b.js
+++ b/assets/git-best-pratices.html-4b6532e6.js
@@ -1,4 +1,4 @@
-import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{r as s,o,c as l,a as e,b as a,d as i,f as t}from"./app-b649ee34.js";const d={},h=t('

2020-09-21

Git最佳实践

精简提交

一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。
如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。

频繁提交

一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。
经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。

此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。

不要提交不完整的改动

虽然原则上来说不要提交一些还没有完成的改动,但是对于一个非常庞大的新功能来说,也并不意味着你必须整体完成这个功能后才可以提交。恰恰相反,你必须把那些改动正确地分割成一些有意义的逻辑模块来进行频繁地提交。

如果你仅仅是因为急着想要下班,或者是想要得到一个干净的工作副本(比如想要切换到另一个分支上),你可以利用 Git 所提供的储藏(Stash)功能来解决这些问题。切记不要把那些不完整的改动提交到仓库中。

提交前测试那些改动

不要理所当然地认为自己完成的改动都是正确的。所有的改动一定要通过彻底地测试才表示它真正地被完成了。

版本控制不是备份系统

版本控制系统具有一个很强大的附带功能,那就是服务器端的备份功能。但是千万不要把 VCS 仅仅当成一个备份系统。特别需要注意的是,只能提交那些有意义的改动。

Github实例

一个功能对应一个分支

下面是好的示例: 格式化代码,也应该单独一个PR
下面是不好的示例:因为一个PR修改了不同的主题内容

提交“瘦”的PR

',18),c={href:"https://deliveroo.engineering/2017/09/06/play-pull-request-roulette.html#ideas-to-make-your-prs-more-review-friendly",target:"_blank",rel:"noopener noreferrer"},g=e("br",null,null,-1),p=e("br",null,null,-1),m=e("br",null,null,-1),u=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/git/1682344998899.png",alt:"",loading:"lazy"},null,-1),b=e("p",null,[a("下图是拆分后:"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/git/1682345004360.png",alt:"",loading:"lazy"})],-1),_=e("p",null,[a("单个PR的改动文件只有11个"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/git/1682345009991.png",alt:"",loading:"lazy"}),a("每个 PR 改动的文件少了,这样 review 起来就更容易了。")],-1),f=e("h3",{id:"使用正确的标题",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#使用正确的标题","aria-hidden":"true"},"#"),a(" 使用正确的标题")],-1),v={href:"https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits",target:"_blank",rel:"noopener noreferrer"},y=t(`

另外,请回答:出于什么原因需要进行这次修改?具体改动了些什么?

  • 使用一定要使用现在时祈使句(例如要使用 change ,而不是 changed 或 changes)。
  • 优先使用正面肯定语句,而不是否定句。

好的示例:docs: extraQuery 的正确使用方法
不好的示例:docs: 更新不直观的例子

根据模板填写PR描述

这是我们 Github 的 PR 模板,融合了我们的最佳实践
下面是实际的好的例子

自动关闭issue

image.png
image.png
git commit -m 'fix #6'
+import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{r as s,o,c as l,a as e,b as a,d as i,f as t}from"./app-a9d55428.js";const d={},h=t('

2020-09-21

Git最佳实践

精简提交

一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。
如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。

频繁提交

一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。
经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。

此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。

不要提交不完整的改动

虽然原则上来说不要提交一些还没有完成的改动,但是对于一个非常庞大的新功能来说,也并不意味着你必须整体完成这个功能后才可以提交。恰恰相反,你必须把那些改动正确地分割成一些有意义的逻辑模块来进行频繁地提交。

如果你仅仅是因为急着想要下班,或者是想要得到一个干净的工作副本(比如想要切换到另一个分支上),你可以利用 Git 所提供的储藏(Stash)功能来解决这些问题。切记不要把那些不完整的改动提交到仓库中。

提交前测试那些改动

不要理所当然地认为自己完成的改动都是正确的。所有的改动一定要通过彻底地测试才表示它真正地被完成了。

版本控制不是备份系统

版本控制系统具有一个很强大的附带功能,那就是服务器端的备份功能。但是千万不要把 VCS 仅仅当成一个备份系统。特别需要注意的是,只能提交那些有意义的改动。

Github实例

一个功能对应一个分支

下面是好的示例: 格式化代码,也应该单独一个PR
下面是不好的示例:因为一个PR修改了不同的主题内容

提交“瘦”的PR

',18),c={href:"https://deliveroo.engineering/2017/09/06/play-pull-request-roulette.html#ideas-to-make-your-prs-more-review-friendly",target:"_blank",rel:"noopener noreferrer"},g=e("br",null,null,-1),p=e("br",null,null,-1),m=e("br",null,null,-1),u=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/git/1682344998899.png",alt:"",loading:"lazy"},null,-1),b=e("p",null,[a("下图是拆分后:"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/git/1682345004360.png",alt:"",loading:"lazy"})],-1),_=e("p",null,[a("单个PR的改动文件只有11个"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/git/1682345009991.png",alt:"",loading:"lazy"}),a("每个 PR 改动的文件少了,这样 review 起来就更容易了。")],-1),f=e("h3",{id:"使用正确的标题",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#使用正确的标题","aria-hidden":"true"},"#"),a(" 使用正确的标题")],-1),v={href:"https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits",target:"_blank",rel:"noopener noreferrer"},y=t(`

另外,请回答:出于什么原因需要进行这次修改?具体改动了些什么?

  • 使用一定要使用现在时祈使句(例如要使用 change ,而不是 changed 或 changes)。
  • 优先使用正面肯定语句,而不是否定句。

好的示例:docs: extraQuery 的正确使用方法
不好的示例:docs: 更新不直观的例子

根据模板填写PR描述

这是我们 Github 的 PR 模板,融合了我们的最佳实践
下面是实际的好的例子

自动关闭issue

image.png
image.png
git commit -m 'fix #6'
 # 或
 git commit -m 'close #6'
 

当pr合并时,将自动close issue

1+2 review 规则

1 是指发起 PR 的人,2 是指进行 code review 的人。也即,每一个 PR,至少要经过两个团队成员 approve 才能合并。

上面是针对 github 的协作,项目组中可酌情变为 1+1 规则


礼貌提问

在 github 向人提问时,需要有礼貌。当提出 feature request时,还要说明自己的情况,尽可能提供更多的信息给对方。

上面的示例有三个重点:

  1. 开头表达感谢
  2. 中间说明己方的使用情况,并给出相应链接
  3. 最后参考业界已有实现,给出一个方案设想,并给出相应链接

学习资源

`,17),x={href:"https://git.oschina.net/progit/",target:"_blank",rel:"noopener noreferrer"},w={href:"https://learngitbranching.js.org/?NODEMO",target:"_blank",rel:"noopener noreferrer"};function k(P,z){const r=s("ExternalLinkIcon");return o(),l("div",null,[h,e("p",null,[a("参考文章:"),e("a",c,[a("https://deliveroo.engineering/2017/09/06/play-pull-request-roulette.html#ideas-to-make-your-prs-more-review-friendly"),i(r)]),g,a(" 其中最重要的一点:不要一次提交一个很大改动的PR,否则别人很难 review,要学会拆分步骤。"),p,a(" 下面是一个 PR 示例:"),m,a(" 拆分前,包含了35个改动,很难 review"),u]),b,_,f,e("p",null,[e("a",v,[a("相关规范看这里"),i(r)])]),y,e("ul",null,[e("li",null,[e("a",x,[a("Pro Git"),i(r)])]),e("li",null,[e("a",w,[a("https://learngitbranching.js.org"),i(r)])])])])}const V=n(d,[["render",k],["__file","git-best-pratices.html.vue"]]);export{V as default}; diff --git a/assets/git-best-pratices.html-4037825e.js b/assets/git-best-pratices.html-9f9a9e0e.js similarity index 93% rename from assets/git-best-pratices.html-4037825e.js rename to assets/git-best-pratices.html-9f9a9e0e.js index 5e39601a..aa99d65e 100644 --- a/assets/git-best-pratices.html-4037825e.js +++ b/assets/git-best-pratices.html-9f9a9e0e.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-1e5872c4","path":"/git/git-best-pratices.html","title":"Git最佳实践","lang":"zh-CN","frontmatter":{"date":"2020-09-21T00:00:00.000Z","tag":"Git","description":"2020-09-21 Git最佳实践 精简提交 一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。 如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。 频繁提交 一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。 经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。 此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/git-best-pratices.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Git最佳实践"}],["meta",{"property":"og:description","content":"2020-09-21 Git最佳实践 精简提交 一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。 如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。 频繁提交 一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。 经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。 此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2020-09-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Git最佳实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2020-09-21T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"精简提交","slug":"精简提交","link":"#精简提交","children":[]},{"level":2,"title":"频繁提交","slug":"频繁提交","link":"#频繁提交","children":[]},{"level":2,"title":"不要提交不完整的改动","slug":"不要提交不完整的改动","link":"#不要提交不完整的改动","children":[]},{"level":2,"title":"提交前测试那些改动","slug":"提交前测试那些改动","link":"#提交前测试那些改动","children":[]},{"level":2,"title":"版本控制不是备份系统","slug":"版本控制不是备份系统","link":"#版本控制不是备份系统","children":[]},{"level":2,"title":"Github实例","slug":"github实例","link":"#github实例","children":[{"level":3,"title":"一个功能对应一个分支","slug":"一个功能对应一个分支","link":"#一个功能对应一个分支","children":[]},{"level":3,"title":"提交“瘦”的PR","slug":"提交-瘦-的pr","link":"#提交-瘦-的pr","children":[]},{"level":3,"title":"使用正确的标题","slug":"使用正确的标题","link":"#使用正确的标题","children":[]},{"level":3,"title":"根据模板填写PR描述","slug":"根据模板填写pr描述","link":"#根据模板填写pr描述","children":[]},{"level":3,"title":"自动关闭issue","slug":"自动关闭issue","link":"#自动关闭issue","children":[]},{"level":3,"title":"1+2 review 规则","slug":"_1-2-review-规则","link":"#_1-2-review-规则","children":[]},{"level":3,"title":"礼貌提问","slug":"礼貌提问","link":"#礼貌提问","children":[]}]},{"level":2,"title":"学习资源","slug":"学习资源","link":"#学习资源","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.86,"words":1158},"filePathRelative":"git/git-best-pratices.md","localizedDate":"2020年9月21日","excerpt":"

2020-09-21

\\n

Git最佳实践

\\n

精简提交

\\n

一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。
\\n如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。

\\n

频繁提交

\\n

一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。
\\n经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。

\\n

此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-1e5872c4","path":"/git/git-best-pratices.html","title":"Git最佳实践","lang":"zh-CN","frontmatter":{"date":"2020-09-21T00:00:00.000Z","tag":"Git","description":"2020-09-21 Git最佳实践 精简提交 一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。 如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。 频繁提交 一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。 经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。 此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/git-best-pratices.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Git最佳实践"}],["meta",{"property":"og:description","content":"2020-09-21 Git最佳实践 精简提交 一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。 如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。 频繁提交 一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。 经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。 此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2020-09-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Git最佳实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2020-09-21T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"精简提交","slug":"精简提交","link":"#精简提交","children":[]},{"level":2,"title":"频繁提交","slug":"频繁提交","link":"#频繁提交","children":[]},{"level":2,"title":"不要提交不完整的改动","slug":"不要提交不完整的改动","link":"#不要提交不完整的改动","children":[]},{"level":2,"title":"提交前测试那些改动","slug":"提交前测试那些改动","link":"#提交前测试那些改动","children":[]},{"level":2,"title":"版本控制不是备份系统","slug":"版本控制不是备份系统","link":"#版本控制不是备份系统","children":[]},{"level":2,"title":"Github实例","slug":"github实例","link":"#github实例","children":[{"level":3,"title":"一个功能对应一个分支","slug":"一个功能对应一个分支","link":"#一个功能对应一个分支","children":[]},{"level":3,"title":"提交“瘦”的PR","slug":"提交-瘦-的pr","link":"#提交-瘦-的pr","children":[]},{"level":3,"title":"使用正确的标题","slug":"使用正确的标题","link":"#使用正确的标题","children":[]},{"level":3,"title":"根据模板填写PR描述","slug":"根据模板填写pr描述","link":"#根据模板填写pr描述","children":[]},{"level":3,"title":"自动关闭issue","slug":"自动关闭issue","link":"#自动关闭issue","children":[]},{"level":3,"title":"1+2 review 规则","slug":"_1-2-review-规则","link":"#_1-2-review-规则","children":[]},{"level":3,"title":"礼貌提问","slug":"礼貌提问","link":"#礼貌提问","children":[]}]},{"level":2,"title":"学习资源","slug":"学习资源","link":"#学习资源","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.86,"words":1158},"filePathRelative":"git/git-best-pratices.md","localizedDate":"2020年9月21日","excerpt":"

2020-09-21

\\n

Git最佳实践

\\n

精简提交

\\n

一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。
\\n如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。

\\n

频繁提交

\\n

一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。
\\n经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。

\\n

此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。

","autoDesc":true}');export{e as data}; diff --git a/assets/git-definitive-guide-to-merge-code.html-06d295be.js b/assets/git-definitive-guide-to-merge-code.html-88801d1b.js similarity index 93% rename from assets/git-definitive-guide-to-merge-code.html-06d295be.js rename to assets/git-definitive-guide-to-merge-code.html-88801d1b.js index 6d55daf6..483e591f 100644 --- a/assets/git-definitive-guide-to-merge-code.html-06d295be.js +++ b/assets/git-definitive-guide-to-merge-code.html-88801d1b.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-48e494ef","path":"/git/git-definitive-guide-to-merge-code.html","title":"Git代码合并指南","lang":"zh-CN","frontmatter":{"date":"2022-04-28T00:00:00.000Z","tag":["Git"],"description":"Git代码合并指南 前言 合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。 在说明问题前,先定义一些概念: feat:指代功能分支 dev 与 test:指代两条不同的长驻分支,它们具有以下特点: 受保护,不能直接推送 不会被删除 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test MR:merge request。代码合并请求","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/git-definitive-guide-to-merge-code.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Git代码合并指南"}],["meta",{"property":"og:description","content":"Git代码合并指南 前言 合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。 在说明问题前,先定义一些概念: feat:指代功能分支 dev 与 test:指代两条不同的长驻分支,它们具有以下特点: 受保护,不能直接推送 不会被删除 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test MR:merge request。代码合并请求"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2022-04-28T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Git代码合并指南\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-04-28T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"功能分支合并长驻分支冲突","slug":"功能分支合并长驻分支冲突","link":"#功能分支合并长驻分支冲突","children":[{"level":3,"title":"解决思路","slug":"解决思路","link":"#解决思路","children":[]},{"level":3,"title":"操作步骤","slug":"操作步骤","link":"#操作步骤","children":[]}]},{"level":2,"title":"功能分支被污染","slug":"功能分支被污染","link":"#功能分支被污染","children":[{"level":3,"title":"解决思路","slug":"解决思路-1","link":"#解决思路-1","children":[]},{"level":3,"title":"操作步骤","slug":"操作步骤-1","link":"#操作步骤-1","children":[]}]},{"level":2,"title":"挑选别的分支部分代码合并","slug":"挑选别的分支部分代码合并","link":"#挑选别的分支部分代码合并","children":[{"level":3,"title":"解决思路","slug":"解决思路-2","link":"#解决思路-2","children":[]},{"level":3,"title":"操作步骤","slug":"操作步骤-2","link":"#操作步骤-2","children":[]}]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.45,"words":1035},"filePathRelative":"git/git-definitive-guide-to-merge-code.md","localizedDate":"2022年4月28日","excerpt":"

Git代码合并指南

\\n

前言

\\n

合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。
\\n在说明问题前,先定义一些概念:

\\n
    \\n
  • feat:指代功能分支
  • \\n
  • dev 与 test:指代两条不同的长驻分支,它们具有以下特点:\\n
      \\n
    • 受保护,不能直接推送
    • \\n
    • 不会被删除
    • \\n
    • 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test
    • \\n
    \\n
  • \\n
  • MR:merge request。代码合并请求
  • \\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-48e494ef","path":"/git/git-definitive-guide-to-merge-code.html","title":"Git代码合并指南","lang":"zh-CN","frontmatter":{"date":"2022-04-28T00:00:00.000Z","tag":["Git"],"description":"Git代码合并指南 前言 合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。 在说明问题前,先定义一些概念: feat:指代功能分支 dev 与 test:指代两条不同的长驻分支,它们具有以下特点: 受保护,不能直接推送 不会被删除 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test MR:merge request。代码合并请求","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/git-definitive-guide-to-merge-code.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Git代码合并指南"}],["meta",{"property":"og:description","content":"Git代码合并指南 前言 合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。 在说明问题前,先定义一些概念: feat:指代功能分支 dev 与 test:指代两条不同的长驻分支,它们具有以下特点: 受保护,不能直接推送 不会被删除 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test MR:merge request。代码合并请求"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2022-04-28T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Git代码合并指南\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-04-28T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"功能分支合并长驻分支冲突","slug":"功能分支合并长驻分支冲突","link":"#功能分支合并长驻分支冲突","children":[{"level":3,"title":"解决思路","slug":"解决思路","link":"#解决思路","children":[]},{"level":3,"title":"操作步骤","slug":"操作步骤","link":"#操作步骤","children":[]}]},{"level":2,"title":"功能分支被污染","slug":"功能分支被污染","link":"#功能分支被污染","children":[{"level":3,"title":"解决思路","slug":"解决思路-1","link":"#解决思路-1","children":[]},{"level":3,"title":"操作步骤","slug":"操作步骤-1","link":"#操作步骤-1","children":[]}]},{"level":2,"title":"挑选别的分支部分代码合并","slug":"挑选别的分支部分代码合并","link":"#挑选别的分支部分代码合并","children":[{"level":3,"title":"解决思路","slug":"解决思路-2","link":"#解决思路-2","children":[]},{"level":3,"title":"操作步骤","slug":"操作步骤-2","link":"#操作步骤-2","children":[]}]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.45,"words":1035},"filePathRelative":"git/git-definitive-guide-to-merge-code.md","localizedDate":"2022年4月28日","excerpt":"

Git代码合并指南

\\n

前言

\\n

合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。
\\n在说明问题前,先定义一些概念:

\\n
    \\n
  • feat:指代功能分支
  • \\n
  • dev 与 test:指代两条不同的长驻分支,它们具有以下特点:\\n
      \\n
    • 受保护,不能直接推送
    • \\n
    • 不会被删除
    • \\n
    • 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test
    • \\n
    \\n
  • \\n
  • MR:merge request。代码合并请求
  • \\n
","autoDesc":true}');export{e as data}; diff --git a/assets/git-definitive-guide-to-merge-code.html-39271ae4.js b/assets/git-definitive-guide-to-merge-code.html-abe98718.js similarity index 99% rename from assets/git-definitive-guide-to-merge-code.html-39271ae4.js rename to assets/git-definitive-guide-to-merge-code.html-abe98718.js index 235e0b10..4dba67a1 100644 --- a/assets/git-definitive-guide-to-merge-code.html-39271ae4.js +++ b/assets/git-definitive-guide-to-merge-code.html-abe98718.js @@ -1 +1 @@ -import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{o as e,c as a,f as l}from"./app-b649ee34.js";const t={},r=l('

Git代码合并指南

前言

合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。
在说明问题前,先定义一些概念:

  • feat:指代功能分支
  • dev 与 test:指代两条不同的长驻分支,它们具有以下特点:
    • 受保护,不能直接推送
    • 不会被删除
    • 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test
  • MR:merge request。代码合并请求

以及说明本文解决冲突涉及到的工具及平台:

  • 使用 IDEA 解决冲突(JetBrains系列的工具都适用)
  • 使用 GitLab 托管代码

功能分支合并长驻分支冲突

这是最常见的场景:feat1 与 feat2 并行开发,当提交MR( feat1 -> dev )时,发现冲突了,无法合并。
下面先给出解决思路,再给出图文操作步骤。

解决思路

  1. 因为合并的方向是 feat -> dev,所以解决冲突应该是在本地 dev 合并 feat
  2. 又因为本地 dev 不能向远程推送,因而需要基于 dev 切一个新分支 conflict/resolved
  3. 推送 conflict/resolved 分支
  4. 提交 MR:conflict/resolved -> dev

操作步骤

  1. 本地切换到 dev 分支,更新代码
  2. 合并相应的 feat 分支
  1. 弹出冲突提示,点击合并
  1. 首先处理无冲突的代码,点击下图红框处
  1. 再根据情况,选择合并代码或丢弃代码。
  1. 在 dev 分支上切出新分支,推荐命名为 conflict/xxx
  1. 推送代码,提交MR(conflict -> dev),记得勾选合并后删除

功能分支被污染

分支一多,人难免失误,很可能造成 feat 分支被污染,即当提MR(feat -> test)时,出现不想合并到 test的代码或提交记录。
这种场景的出现可能有多种原因:

  1. 研发过程中出现误操作,如出现了 dev -> feat 的合并
  2. feat 分支的基线分支搞错了,如从 dev 切出了 feat

解决思路

  1. 基于目标分支如 (test 分支)切一个干净的分支 clean
  2. 使用 cherry-pick,挑选自己想要的提交
  3. 再提交MR(clean -> test)

注意的是,要按提交顺序进行 cherry-pick,以避免遗漏或出错。

操作步骤

  1. 更新目标分支(在这里是 test)
  1. 基于 test 切新分支,这里示例命名为:clean
  1. 在拥有最新代码的分支(这里是 feat) 找到并选中相应的提交记录
  2. 右键,点击 Cherry-Pick
  3. 则相应的提交记录就会合并到 clean 分支
  4. 推送 clean,提交MR(clean -> test)

挑选别的分支部分代码合并

有可能会出现这样一种场景:

  • 最新的生产代码里,假设版本为v1.3.0,包含了 feat 分支的代码
  • 为了减少分支的冗余,代码一旦上生产后,就会清除相应的功能分支,也即此时仓库里没有 feat 分支了
  • 客户方部署的版本代码为 v1.1.0,而客户不想升级到最新的版本,只想要 feat 分支相应的功能

此时该如何是好?

解决思路

其实只要触发“挑选”关键字,就可以考虑使用 cherry-pick。
feat 分支就算被删了,只要提交记录还在,那也没关系:

  • 在v1.3.0 的代码库中,按分支筛选,找出 feat 分支对应的提交记录
  • 通过 cherry-pick 把 feat 分支的代码合并到客户方的代码分支即可

注意:毕竟跨越了版本,无法保证合并过去后的代码一定能正确工作,需要进行充分地测试。

操作步骤

此操作本质还是 cherry-pick,参考前面 cherry-pick 的示例即可。

',45),o=[r];function d(c,n){return e(),a("div",null,o)}const f=i(t,[["render",d],["__file","git-definitive-guide-to-merge-code.html.vue"]]);export{f as default}; +import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{o as e,c as a,f as l}from"./app-a9d55428.js";const t={},r=l('

Git代码合并指南

前言

合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。
在说明问题前,先定义一些概念:

  • feat:指代功能分支
  • dev 与 test:指代两条不同的长驻分支,它们具有以下特点:
    • 受保护,不能直接推送
    • 不会被删除
    • 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test
  • MR:merge request。代码合并请求

以及说明本文解决冲突涉及到的工具及平台:

  • 使用 IDEA 解决冲突(JetBrains系列的工具都适用)
  • 使用 GitLab 托管代码

功能分支合并长驻分支冲突

这是最常见的场景:feat1 与 feat2 并行开发,当提交MR( feat1 -> dev )时,发现冲突了,无法合并。
下面先给出解决思路,再给出图文操作步骤。

解决思路

  1. 因为合并的方向是 feat -> dev,所以解决冲突应该是在本地 dev 合并 feat
  2. 又因为本地 dev 不能向远程推送,因而需要基于 dev 切一个新分支 conflict/resolved
  3. 推送 conflict/resolved 分支
  4. 提交 MR:conflict/resolved -> dev

操作步骤

  1. 本地切换到 dev 分支,更新代码
  2. 合并相应的 feat 分支
  1. 弹出冲突提示,点击合并
  1. 首先处理无冲突的代码,点击下图红框处
  1. 再根据情况,选择合并代码或丢弃代码。
  1. 在 dev 分支上切出新分支,推荐命名为 conflict/xxx
  1. 推送代码,提交MR(conflict -> dev),记得勾选合并后删除

功能分支被污染

分支一多,人难免失误,很可能造成 feat 分支被污染,即当提MR(feat -> test)时,出现不想合并到 test的代码或提交记录。
这种场景的出现可能有多种原因:

  1. 研发过程中出现误操作,如出现了 dev -> feat 的合并
  2. feat 分支的基线分支搞错了,如从 dev 切出了 feat

解决思路

  1. 基于目标分支如 (test 分支)切一个干净的分支 clean
  2. 使用 cherry-pick,挑选自己想要的提交
  3. 再提交MR(clean -> test)

注意的是,要按提交顺序进行 cherry-pick,以避免遗漏或出错。

操作步骤

  1. 更新目标分支(在这里是 test)
  1. 基于 test 切新分支,这里示例命名为:clean
  1. 在拥有最新代码的分支(这里是 feat) 找到并选中相应的提交记录
  2. 右键,点击 Cherry-Pick
  3. 则相应的提交记录就会合并到 clean 分支
  4. 推送 clean,提交MR(clean -> test)

挑选别的分支部分代码合并

有可能会出现这样一种场景:

  • 最新的生产代码里,假设版本为v1.3.0,包含了 feat 分支的代码
  • 为了减少分支的冗余,代码一旦上生产后,就会清除相应的功能分支,也即此时仓库里没有 feat 分支了
  • 客户方部署的版本代码为 v1.1.0,而客户不想升级到最新的版本,只想要 feat 分支相应的功能

此时该如何是好?

解决思路

其实只要触发“挑选”关键字,就可以考虑使用 cherry-pick。
feat 分支就算被删了,只要提交记录还在,那也没关系:

  • 在v1.3.0 的代码库中,按分支筛选,找出 feat 分支对应的提交记录
  • 通过 cherry-pick 把 feat 分支的代码合并到客户方的代码分支即可

注意:毕竟跨越了版本,无法保证合并过去后的代码一定能正确工作,需要进行充分地测试。

操作步骤

此操作本质还是 cherry-pick,参考前面 cherry-pick 的示例即可。

',45),o=[r];function d(c,n){return e(),a("div",null,o)}const f=i(t,[["render",d],["__file","git-definitive-guide-to-merge-code.html.vue"]]);export{f as default}; diff --git a/assets/git-history-two-tricks-in-idea.html-fd760def.js b/assets/git-history-two-tricks-in-idea.html-0b65486a.js similarity index 87% rename from assets/git-history-two-tricks-in-idea.html-fd760def.js rename to assets/git-history-two-tricks-in-idea.html-0b65486a.js index 9ac8b959..8e6433db 100644 --- a/assets/git-history-two-tricks-in-idea.html-fd760def.js +++ b/assets/git-history-two-tricks-in-idea.html-0b65486a.js @@ -1 +1 @@ -const t=JSON.parse('{"key":"v-60021cbc","path":"/git/git-history-two-tricks-in-idea.html","title":"Git查看历史记录小技巧","lang":"zh-CN","frontmatter":{"date":"2023-08-11T00:00:00.000Z","tag":["Git"],"description":"Git查看历史记录小技巧 分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。 这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/git-history-two-tricks-in-idea.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Git查看历史记录小技巧"}],["meta",{"property":"og:description","content":"Git查看历史记录小技巧 分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。 这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2023-08-11T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Git查看历史记录小技巧\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-11T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.16,"words":648},"filePathRelative":"git/git-history-two-tricks-in-idea.md","localizedDate":"2023年8月11日","excerpt":"

Git查看历史记录小技巧

\\n

分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。

\\n

这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。

\\n","autoDesc":true}');export{t as data}; +const t=JSON.parse('{"key":"v-60021cbc","path":"/git/git-history-two-tricks-in-idea.html","title":"Git查看历史记录小技巧","lang":"zh-CN","frontmatter":{"date":"2023-08-11T00:00:00.000Z","tag":["Git"],"description":"Git查看历史记录小技巧 分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。 这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/git-history-two-tricks-in-idea.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Git查看历史记录小技巧"}],["meta",{"property":"og:description","content":"Git查看历史记录小技巧 分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。 这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2023-08-11T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Git查看历史记录小技巧\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-11T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.16,"words":648},"filePathRelative":"git/git-history-two-tricks-in-idea.md","localizedDate":"2023年8月11日","excerpt":"

Git查看历史记录小技巧

\\n

分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。

\\n

这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。

\\n","autoDesc":true}');export{t as data}; diff --git a/assets/git-history-two-tricks-in-idea.html-3b5a7689.js b/assets/git-history-two-tricks-in-idea.html-41e54e71.js similarity index 98% rename from assets/git-history-two-tricks-in-idea.html-3b5a7689.js rename to assets/git-history-two-tricks-in-idea.html-41e54e71.js index f82cf458..c4131fe2 100644 --- a/assets/git-history-two-tricks-in-idea.html-3b5a7689.js +++ b/assets/git-history-two-tricks-in-idea.html-41e54e71.js @@ -1 +1 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{o as i,c as a,e as o,a as e,b as r,f as l}from"./app-b649ee34.js";const m={},c=e("h1",{id:"git查看历史记录小技巧",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#git查看历史记录小技巧","aria-hidden":"true"},"#"),r(" Git查看历史记录小技巧")],-1),s=e("p",null,"分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。",-1),n=e("p",null,"这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。",-1),d=l('

第一个是叫 annotate with git blame
在IDEA的行号这个位置,右键,再点击即可。如图所示:

效果就是,每一行代码都会显示,该行代码是由谁提交的、 什么时候提交的。

在合并冲突的时候也可以用这个技巧。

对左右两边进行git blame一下, 然后就可以看到如图所示的情况:

这样就能提供更多的信息帮助解决冲突。

就算冲突无法自己解决,也至少能知道提交代码的是谁,可以找到作者去进行沟通。

当然git blame 是有一些注意点的。因为它本质上显示的是某一行代码的最后提交人, 也就是last modified的一个概念,而有些时候这并不意味着最后的修改人就是代码的原作者。

之所以这样,可能会有以下的原因:

  1. 代码格式化
  2. 移动代码,比如说拷贝代码、迁移代码
  3. 合并代码,解决冲突

上述操作都会改变最后修改人的这个属性,但此时显然最后修改人并非原作者。

这说明,有时候仅知道了某一行代码是由谁最近修改的还不够,还需要知道某一个文件经过了怎么样的修改。

这就引出了第二个小技巧了: git show history

点击IDEA某个文件的空白处,然后右键,选择git,然后点击 show history。

就会出现如图所示的这样的一个 git log的 界面。

那么这样就可以看到这个文件从最初到至今经历过了怎样的修改、 有过哪些人在上面修改, 从而更好的进行记录追踪。

这两个技巧, 其实是越有经验就对你帮助越大的。因为你,年限越长,你看别人的代码的机会就越; 而如果你年限尚浅的话,更多的是你的代码被别人 review。

',16);function p(g,h){return i(),a("div",null,[c,s,n,o(" more "),d])}const b=t(m,[["render",p],["__file","git-history-two-tricks-in-idea.html.vue"]]);export{b as default}; +import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{o as i,c as a,e as o,a as e,b as r,f as l}from"./app-a9d55428.js";const m={},c=e("h1",{id:"git查看历史记录小技巧",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#git查看历史记录小技巧","aria-hidden":"true"},"#"),r(" Git查看历史记录小技巧")],-1),s=e("p",null,"分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。",-1),n=e("p",null,"这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。",-1),d=l('

第一个是叫 annotate with git blame
在IDEA的行号这个位置,右键,再点击即可。如图所示:

效果就是,每一行代码都会显示,该行代码是由谁提交的、 什么时候提交的。

在合并冲突的时候也可以用这个技巧。

对左右两边进行git blame一下, 然后就可以看到如图所示的情况:

这样就能提供更多的信息帮助解决冲突。

就算冲突无法自己解决,也至少能知道提交代码的是谁,可以找到作者去进行沟通。

当然git blame 是有一些注意点的。因为它本质上显示的是某一行代码的最后提交人, 也就是last modified的一个概念,而有些时候这并不意味着最后的修改人就是代码的原作者。

之所以这样,可能会有以下的原因:

  1. 代码格式化
  2. 移动代码,比如说拷贝代码、迁移代码
  3. 合并代码,解决冲突

上述操作都会改变最后修改人的这个属性,但此时显然最后修改人并非原作者。

这说明,有时候仅知道了某一行代码是由谁最近修改的还不够,还需要知道某一个文件经过了怎么样的修改。

这就引出了第二个小技巧了: git show history

点击IDEA某个文件的空白处,然后右键,选择git,然后点击 show history。

就会出现如图所示的这样的一个 git log的 界面。

那么这样就可以看到这个文件从最初到至今经历过了怎样的修改、 有过哪些人在上面修改, 从而更好的进行记录追踪。

这两个技巧, 其实是越有经验就对你帮助越大的。因为你,年限越长,你看别人的代码的机会就越; 而如果你年限尚浅的话,更多的是你的代码被别人 review。

',16);function p(g,h){return i(),a("div",null,[c,s,n,o(" more "),d])}const b=t(m,[["render",p],["__file","git-history-two-tricks-in-idea.html.vue"]]);export{b as default}; diff --git a/assets/git-useful-commands.html-2e056050.js b/assets/git-useful-commands.html-3d5dd23d.js similarity index 95% rename from assets/git-useful-commands.html-2e056050.js rename to assets/git-useful-commands.html-3d5dd23d.js index 1d4c9814..369fa3fe 100644 --- a/assets/git-useful-commands.html-2e056050.js +++ b/assets/git-useful-commands.html-3d5dd23d.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-642eaaea","path":"/git/git-useful-commands.html","title":"Git常用命令","lang":"zh-CN","frontmatter":{"date":"2020-09-21T00:00:00.000Z","tag":"Git","description":"Git常用命令 前言 本文将列举Git常见场景,并给出相应解决方案。 约定: 下文代码块中${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。 推荐: 图形化交互式Git教程 配置 Mac/Linux 用户 执行以下操作 vi ~/.gitconfig","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/git-useful-commands.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Git常用命令"}],["meta",{"property":"og:description","content":"Git常用命令 前言 本文将列举Git常见场景,并给出相应解决方案。 约定: 下文代码块中${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。 推荐: 图形化交互式Git教程 配置 Mac/Linux 用户 执行以下操作 vi ~/.gitconfig"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2020-09-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Git常用命令\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2020-09-21T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"配置","slug":"配置","link":"#配置","children":[]},{"level":2,"title":"增强","slug":"增强","link":"#增强","children":[]},{"level":2,"title":"记住账号密码","slug":"记住账号密码","link":"#记住账号密码","children":[]},{"level":2,"title":"初始化","slug":"初始化","link":"#初始化","children":[]},{"level":2,"title":"本地提交","slug":"本地提交","link":"#本地提交","children":[{"level":3,"title":"取消未暂存的修改","slug":"取消未暂存的修改","link":"#取消未暂存的修改","children":[]},{"level":3,"title":"取消add","slug":"取消add","link":"#取消add","children":[]},{"level":3,"title":"取消提交","slug":"取消提交","link":"#取消提交","children":[]},{"level":3,"title":"修正提交","slug":"修正提交","link":"#修正提交","children":[]},{"level":3,"title":"stash修改","slug":"stash修改","link":"#stash修改","children":[]},{"level":3,"title":"恢复stash","slug":"恢复stash","link":"#恢复stash","children":[]}]},{"level":2,"title":"分支管理","slug":"分支管理","link":"#分支管理","children":[{"level":3,"title":"创建分支","slug":"创建分支","link":"#创建分支","children":[]},{"level":3,"title":"查看远程分支","slug":"查看远程分支","link":"#查看远程分支","children":[]},{"level":3,"title":"创建干净历史分支","slug":"创建干净历史分支","link":"#创建干净历史分支","children":[]},{"level":3,"title":"删除分支","slug":"删除分支","link":"#删除分支","children":[]}]},{"level":2,"title":"远程仓库","slug":"远程仓库","link":"#远程仓库","children":[{"level":3,"title":"远程仓库管理","slug":"远程仓库管理","link":"#远程仓库管理","children":[]},{"level":3,"title":"浅克隆","slug":"浅克隆","link":"#浅克隆","children":[]},{"level":3,"title":"克隆指定分支","slug":"克隆指定分支","link":"#克隆指定分支","children":[]},{"level":3,"title":"克隆失败因为文件名太长","slug":"克隆失败因为文件名太长","link":"#克隆失败因为文件名太长","children":[]},{"level":3,"title":"强行推送","slug":"强行推送","link":"#强行推送","children":[]},{"level":3,"title":"取消错误的推送","slug":"取消错误的推送","link":"#取消错误的推送","children":[]}]},{"level":2,"title":"标签管理","slug":"标签管理","link":"#标签管理","children":[{"level":3,"title":"新建本地标签","slug":"新建本地标签","link":"#新建本地标签","children":[]},{"level":3,"title":"删除本地标签","slug":"删除本地标签","link":"#删除本地标签","children":[]},{"level":3,"title":"查看本地所有标签","slug":"查看本地所有标签","link":"#查看本地所有标签","children":[]},{"level":3,"title":"推送本地标签","slug":"推送本地标签","link":"#推送本地标签","children":[]},{"level":3,"title":"获取远程标签","slug":"获取远程标签","link":"#获取远程标签","children":[]},{"level":3,"title":"删除远程标签","slug":"删除远程标签","link":"#删除远程标签","children":[]}]},{"level":2,"title":"其他","slug":"其他","link":"#其他","children":[{"level":3,"title":"cherry-pick","slug":"cherry-pick","link":"#cherry-pick","children":[]},{"level":3,"title":"merge unrelated histories","slug":"merge-unrelated-histories","link":"#merge-unrelated-histories","children":[]},{"level":3,"title":"git log 丢失最新提交","slug":"git-log-丢失最新提交","link":"#git-log-丢失最新提交","children":[]},{"level":3,"title":"查看分支创建时间","slug":"查看分支创建时间","link":"#查看分支创建时间","children":[]},{"level":3,"title":"根据文件搜索历史","slug":"根据文件搜索历史","link":"#根据文件搜索历史","children":[]},{"level":3,"title":"从所有提交中删除一个文件","slug":"从所有提交中删除一个文件","link":"#从所有提交中删除一个文件","children":[]}]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5.05,"words":1515},"filePathRelative":"git/git-useful-commands.md","localizedDate":"2020年9月21日","excerpt":"

Git常用命令

\\n

前言

\\n

本文将列举Git常见场景,并给出相应解决方案。

\\n

约定: 下文代码块中${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。

\\n

推荐: 图形化交互式Git教程

\\n

配置

\\n

Mac/Linux 用户 执行以下操作

\\n
vi ~/.gitconfig\\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-642eaaea","path":"/git/git-useful-commands.html","title":"Git常用命令","lang":"zh-CN","frontmatter":{"date":"2020-09-21T00:00:00.000Z","tag":"Git","description":"Git常用命令 前言 本文将列举Git常见场景,并给出相应解决方案。 约定: 下文代码块中${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。 推荐: 图形化交互式Git教程 配置 Mac/Linux 用户 执行以下操作 vi ~/.gitconfig","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/git-useful-commands.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Git常用命令"}],["meta",{"property":"og:description","content":"Git常用命令 前言 本文将列举Git常见场景,并给出相应解决方案。 约定: 下文代码块中${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。 推荐: 图形化交互式Git教程 配置 Mac/Linux 用户 执行以下操作 vi ~/.gitconfig"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2020-09-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Git常用命令\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2020-09-21T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"配置","slug":"配置","link":"#配置","children":[]},{"level":2,"title":"增强","slug":"增强","link":"#增强","children":[]},{"level":2,"title":"记住账号密码","slug":"记住账号密码","link":"#记住账号密码","children":[]},{"level":2,"title":"初始化","slug":"初始化","link":"#初始化","children":[]},{"level":2,"title":"本地提交","slug":"本地提交","link":"#本地提交","children":[{"level":3,"title":"取消未暂存的修改","slug":"取消未暂存的修改","link":"#取消未暂存的修改","children":[]},{"level":3,"title":"取消add","slug":"取消add","link":"#取消add","children":[]},{"level":3,"title":"取消提交","slug":"取消提交","link":"#取消提交","children":[]},{"level":3,"title":"修正提交","slug":"修正提交","link":"#修正提交","children":[]},{"level":3,"title":"stash修改","slug":"stash修改","link":"#stash修改","children":[]},{"level":3,"title":"恢复stash","slug":"恢复stash","link":"#恢复stash","children":[]}]},{"level":2,"title":"分支管理","slug":"分支管理","link":"#分支管理","children":[{"level":3,"title":"创建分支","slug":"创建分支","link":"#创建分支","children":[]},{"level":3,"title":"查看远程分支","slug":"查看远程分支","link":"#查看远程分支","children":[]},{"level":3,"title":"创建干净历史分支","slug":"创建干净历史分支","link":"#创建干净历史分支","children":[]},{"level":3,"title":"删除分支","slug":"删除分支","link":"#删除分支","children":[]}]},{"level":2,"title":"远程仓库","slug":"远程仓库","link":"#远程仓库","children":[{"level":3,"title":"远程仓库管理","slug":"远程仓库管理","link":"#远程仓库管理","children":[]},{"level":3,"title":"浅克隆","slug":"浅克隆","link":"#浅克隆","children":[]},{"level":3,"title":"克隆指定分支","slug":"克隆指定分支","link":"#克隆指定分支","children":[]},{"level":3,"title":"克隆失败因为文件名太长","slug":"克隆失败因为文件名太长","link":"#克隆失败因为文件名太长","children":[]},{"level":3,"title":"强行推送","slug":"强行推送","link":"#强行推送","children":[]},{"level":3,"title":"取消错误的推送","slug":"取消错误的推送","link":"#取消错误的推送","children":[]}]},{"level":2,"title":"标签管理","slug":"标签管理","link":"#标签管理","children":[{"level":3,"title":"新建本地标签","slug":"新建本地标签","link":"#新建本地标签","children":[]},{"level":3,"title":"删除本地标签","slug":"删除本地标签","link":"#删除本地标签","children":[]},{"level":3,"title":"查看本地所有标签","slug":"查看本地所有标签","link":"#查看本地所有标签","children":[]},{"level":3,"title":"推送本地标签","slug":"推送本地标签","link":"#推送本地标签","children":[]},{"level":3,"title":"获取远程标签","slug":"获取远程标签","link":"#获取远程标签","children":[]},{"level":3,"title":"删除远程标签","slug":"删除远程标签","link":"#删除远程标签","children":[]}]},{"level":2,"title":"其他","slug":"其他","link":"#其他","children":[{"level":3,"title":"cherry-pick","slug":"cherry-pick","link":"#cherry-pick","children":[]},{"level":3,"title":"merge unrelated histories","slug":"merge-unrelated-histories","link":"#merge-unrelated-histories","children":[]},{"level":3,"title":"git log 丢失最新提交","slug":"git-log-丢失最新提交","link":"#git-log-丢失最新提交","children":[]},{"level":3,"title":"查看分支创建时间","slug":"查看分支创建时间","link":"#查看分支创建时间","children":[]},{"level":3,"title":"根据文件搜索历史","slug":"根据文件搜索历史","link":"#根据文件搜索历史","children":[]},{"level":3,"title":"从所有提交中删除一个文件","slug":"从所有提交中删除一个文件","link":"#从所有提交中删除一个文件","children":[]}]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5.05,"words":1515},"filePathRelative":"git/git-useful-commands.md","localizedDate":"2020年9月21日","excerpt":"

Git常用命令

\\n

前言

\\n

本文将列举Git常见场景,并给出相应解决方案。

\\n

约定: 下文代码块中${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。

\\n

推荐: 图形化交互式Git教程

\\n

配置

\\n

Mac/Linux 用户 执行以下操作

\\n
vi ~/.gitconfig\\n
","autoDesc":true}');export{e as data}; diff --git a/assets/git-useful-commands.html-17a54d4f.js b/assets/git-useful-commands.html-517e5b83.js similarity index 99% rename from assets/git-useful-commands.html-17a54d4f.js rename to assets/git-useful-commands.html-517e5b83.js index d5d99a5a..0842066f 100644 --- a/assets/git-useful-commands.html-17a54d4f.js +++ b/assets/git-useful-commands.html-517e5b83.js @@ -1,4 +1,4 @@ -import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as d,o as r,c,a,b as s,d as e,f as i}from"./app-b649ee34.js";const t={},o=a("h1",{id:"git常用命令",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#git常用命令","aria-hidden":"true"},"#"),s(" Git常用命令")],-1),p=a("h2",{id:"前言",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),s(" 前言")],-1),u=a("p",null,"本文将列举Git常见场景,并给出相应解决方案。",-1),h=a("p",null,[s("约定: 下文代码块中"),a("code",null,"${}"),s("里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。")],-1),b={href:"https://learngitbranching.js.org/?locale=zh_CN",target:"_blank",rel:"noopener noreferrer"},v=i(`

配置

Mac/Linux 用户 执行以下操作

vi ~/.gitconfig
+import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as d,o as r,c,a,b as s,d as e,f as i}from"./app-a9d55428.js";const t={},o=a("h1",{id:"git常用命令",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#git常用命令","aria-hidden":"true"},"#"),s(" Git常用命令")],-1),p=a("h2",{id:"前言",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),s(" 前言")],-1),u=a("p",null,"本文将列举Git常见场景,并给出相应解决方案。",-1),h=a("p",null,[s("约定: 下文代码块中"),a("code",null,"${}"),s("里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。")],-1),b={href:"https://learngitbranching.js.org/?locale=zh_CN",target:"_blank",rel:"noopener noreferrer"},v=i(`

配置

Mac/Linux 用户 执行以下操作

vi ~/.gitconfig
 

Windows用户在桌面用户文件夹下有个.gitconfig隐藏文件,直接修改即可

补充以下内容

[alias]
   st = status
   cm = commit
diff --git a/assets/gitlab-ci.html-e4938506.js b/assets/gitlab-ci.html-01cc0cbe.js
similarity index 99%
rename from assets/gitlab-ci.html-e4938506.js
rename to assets/gitlab-ci.html-01cc0cbe.js
index d4e48052..27286b85 100644
--- a/assets/gitlab-ci.html-e4938506.js
+++ b/assets/gitlab-ci.html-01cc0cbe.js
@@ -1,4 +1,4 @@
-import{_ as c}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o,c as r,e as u,a as n,b as a,d as s,w as p,f as t}from"./app-b649ee34.js";const d={},m=n("h1",{id:"gitlab-ci",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#gitlab-ci","aria-hidden":"true"},"#"),a(" GitLab CI")],-1),k=n("h2",{id:"前言",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),a(" 前言")],-1),g=n("p",null,"GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。",-1),v=t(`

安装与配置

GitLab Runner 安装

进行 Gitlab CI 的第一步是要安装 GitLab Runner。如果公司、团队内部已安装过,可以跳过这一步。

这里推荐使用 docker 的方式安装,复制以下命令执行即可:

docker run -d --name gitlab-runner --restart always \\
+import{_ as c}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o,c as r,e as u,a as n,b as a,d as s,w as p,f as t}from"./app-a9d55428.js";const d={},m=n("h1",{id:"gitlab-ci",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#gitlab-ci","aria-hidden":"true"},"#"),a(" GitLab CI")],-1),k=n("h2",{id:"前言",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),a(" 前言")],-1),g=n("p",null,"GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。",-1),v=t(`

安装与配置

GitLab Runner 安装

进行 Gitlab CI 的第一步是要安装 GitLab Runner。如果公司、团队内部已安装过,可以跳过这一步。

这里推荐使用 docker 的方式安装,复制以下命令执行即可:

docker run -d --name gitlab-runner --restart always \\
   -v /var/run/docker.sock:/var/run/docker.sock \\
   -v /srv/gitlab-runner/config:/etc/gitlab-runner \\
   gitlab/gitlab-runner:latest
diff --git a/assets/gitlab-ci.html-2b32bfd2.js b/assets/gitlab-ci.html-99056a64.js
similarity index 92%
rename from assets/gitlab-ci.html-2b32bfd2.js
rename to assets/gitlab-ci.html-99056a64.js
index 171650fb..ae9f4cc9 100644
--- a/assets/gitlab-ci.html-2b32bfd2.js
+++ b/assets/gitlab-ci.html-99056a64.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-4008ff77","path":"/git/gitlab-ci.html","title":"GitLab CI","lang":"zh-CN","frontmatter":{"date":"2023-01-10T00:00:00.000Z","tag":["Git","GitLab","Java","Node.js"],"description":"GitLab CI 前言 GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/gitlab-ci.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"GitLab CI"}],["meta",{"property":"og:description","content":"GitLab CI 前言 GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:tag","content":"GitLab"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:published_time","content":"2023-01-10T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"GitLab CI\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-01-10T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"安装与配置","slug":"安装与配置","link":"#安装与配置","children":[{"level":3,"title":"GitLab Runner 安装","slug":"gitlab-runner-安装","link":"#gitlab-runner-安装","children":[]},{"level":3,"title":"GitLab Runner 注册","slug":"gitlab-runner-注册","link":"#gitlab-runner-注册","children":[]},{"level":3,"title":"提交.gitlab-ci.yml","slug":"提交-gitlab-ci-yml","link":"#提交-gitlab-ci-yml","children":[]}]},{"level":2,"title":"合并代码前进行检查","slug":"合并代码前进行检查","link":"#合并代码前进行检查","children":[{"level":3,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":3,"title":"设置MR检查","slug":"设置mr检查","link":"#设置mr检查","children":[]},{"level":3,"title":".gitlab-ci.yml 示例","slug":"gitlab-ci-yml-示例","link":"#gitlab-ci-yml-示例","children":[]},{"level":3,"title":"效果","slug":"效果","link":"#效果","children":[]}]},{"level":2,"title":"集成单元测试","slug":"集成单元测试","link":"#集成单元测试","children":[]},{"level":2,"title":"线上发布 jar","slug":"线上发布-jar","link":"#线上发布-jar","children":[{"level":3,"title":"Maven配置","slug":"maven配置","link":"#maven配置","children":[]},{"level":3,"title":".gitlab-ci.yml 配置","slug":"gitlab-ci-yml-配置","link":"#gitlab-ci-yml-配置","children":[]},{"level":3,"title":"拉取最新的jar","slug":"拉取最新的jar","link":"#拉取最新的jar","children":[]}]},{"level":2,"title":"保存中间产物","slug":"保存中间产物","link":"#保存中间产物","children":[]},{"level":2,"title":"其他问题与解决方案","slug":"其他问题与解决方案","link":"#其他问题与解决方案","children":[{"level":3,"title":"Node.js","slug":"node-js","link":"#node-js","children":[]},{"level":3,"title":"创建不了容器","slug":"创建不了容器","link":"#创建不了容器","children":[]},{"level":3,"title":"本地成功,流水线失败","slug":"本地成功-流水线失败","link":"#本地成功-流水线失败","children":[]}]},{"level":2,"title":"参考文档","slug":"参考文档","link":"#参考文档","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":6.49,"words":1946},"filePathRelative":"git/gitlab-ci.md","localizedDate":"2023年1月10日","excerpt":"

GitLab CI

\\n

前言

\\n

GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-4008ff77","path":"/git/gitlab-ci.html","title":"GitLab CI","lang":"zh-CN","frontmatter":{"date":"2023-01-10T00:00:00.000Z","tag":["Git","GitLab","Java","Node.js"],"description":"GitLab CI 前言 GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/gitlab-ci.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"GitLab CI"}],["meta",{"property":"og:description","content":"GitLab CI 前言 GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:tag","content":"GitLab"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:published_time","content":"2023-01-10T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"GitLab CI\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-01-10T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"安装与配置","slug":"安装与配置","link":"#安装与配置","children":[{"level":3,"title":"GitLab Runner 安装","slug":"gitlab-runner-安装","link":"#gitlab-runner-安装","children":[]},{"level":3,"title":"GitLab Runner 注册","slug":"gitlab-runner-注册","link":"#gitlab-runner-注册","children":[]},{"level":3,"title":"提交.gitlab-ci.yml","slug":"提交-gitlab-ci-yml","link":"#提交-gitlab-ci-yml","children":[]}]},{"level":2,"title":"合并代码前进行检查","slug":"合并代码前进行检查","link":"#合并代码前进行检查","children":[{"level":3,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":3,"title":"设置MR检查","slug":"设置mr检查","link":"#设置mr检查","children":[]},{"level":3,"title":".gitlab-ci.yml 示例","slug":"gitlab-ci-yml-示例","link":"#gitlab-ci-yml-示例","children":[]},{"level":3,"title":"效果","slug":"效果","link":"#效果","children":[]}]},{"level":2,"title":"集成单元测试","slug":"集成单元测试","link":"#集成单元测试","children":[]},{"level":2,"title":"线上发布 jar","slug":"线上发布-jar","link":"#线上发布-jar","children":[{"level":3,"title":"Maven配置","slug":"maven配置","link":"#maven配置","children":[]},{"level":3,"title":".gitlab-ci.yml 配置","slug":"gitlab-ci-yml-配置","link":"#gitlab-ci-yml-配置","children":[]},{"level":3,"title":"拉取最新的jar","slug":"拉取最新的jar","link":"#拉取最新的jar","children":[]}]},{"level":2,"title":"保存中间产物","slug":"保存中间产物","link":"#保存中间产物","children":[]},{"level":2,"title":"其他问题与解决方案","slug":"其他问题与解决方案","link":"#其他问题与解决方案","children":[{"level":3,"title":"Node.js","slug":"node-js","link":"#node-js","children":[]},{"level":3,"title":"创建不了容器","slug":"创建不了容器","link":"#创建不了容器","children":[]},{"level":3,"title":"本地成功,流水线失败","slug":"本地成功-流水线失败","link":"#本地成功-流水线失败","children":[]}]},{"level":2,"title":"参考文档","slug":"参考文档","link":"#参考文档","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":6.49,"words":1946},"filePathRelative":"git/gitlab-ci.md","localizedDate":"2023年1月10日","excerpt":"

GitLab CI

\\n

前言

\\n

GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/how-to-connect-to-internet.html-30b7db89.js b/assets/how-to-connect-to-internet.html-0a649e4e.js similarity index 93% rename from assets/how-to-connect-to-internet.html-30b7db89.js rename to assets/how-to-connect-to-internet.html-0a649e4e.js index afe68b2b..c4b2c255 100644 --- a/assets/how-to-connect-to-internet.html-30b7db89.js +++ b/assets/how-to-connect-to-internet.html-0a649e4e.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-17809471","path":"/tools/how-to-connect-to-internet.html","title":"科学上网","lang":"zh-CN","frontmatter":{"date":"2022-06-20T00:00:00.000Z","tag":"Tool","description":"科学上网 说明 AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍? 本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。 购买指南 点击进入服务页面, 看到如下页面:","head":[["meta",{"property":"og:url","content":"https://levy.vip/tools/how-to-connect-to-internet.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"科学上网"}],["meta",{"property":"og:description","content":"科学上网 说明 AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍? 本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。 购买指南 点击进入服务页面, 看到如下页面:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Tool"}],["meta",{"property":"article:published_time","content":"2022-06-20T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"科学上网\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-20T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"说明","slug":"说明","link":"#说明","children":[]},{"level":2,"title":"购买指南","slug":"购买指南","link":"#购买指南","children":[]},{"level":2,"title":"客户端","slug":"客户端","link":"#客户端","children":[]},{"level":2,"title":"导入配置","slug":"导入配置","link":"#导入配置","children":[]},{"level":2,"title":"其他","slug":"其他","link":"#其他","children":[{"level":3,"title":"500 内部代理错误","slug":"_500-内部代理错误","link":"#_500-内部代理错误","children":[]},{"level":3,"title":"修改PAC文件","slug":"修改pac文件","link":"#修改pac文件","children":[]},{"level":3,"title":"使用 New Bing","slug":"使用-new-bing","link":"#使用-new-bing","children":[]},{"level":3,"title":"申请美区apple id","slug":"申请美区apple-id","link":"#申请美区apple-id","children":[]}]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.72,"words":815},"filePathRelative":"tools/how-to-connect-to-internet.md","localizedDate":"2022年6月20日","excerpt":"

科学上网

\\n

说明

\\n

AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍?

\\n

本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。

\\n

购买指南

\\n

点击进入服务页面, 看到如下页面:
\\n\\"image.png\\"

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-17809471","path":"/tools/how-to-connect-to-internet.html","title":"科学上网","lang":"zh-CN","frontmatter":{"date":"2022-06-20T00:00:00.000Z","tag":"Tool","description":"科学上网 说明 AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍? 本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。 购买指南 点击进入服务页面, 看到如下页面:","head":[["meta",{"property":"og:url","content":"https://levy.vip/tools/how-to-connect-to-internet.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"科学上网"}],["meta",{"property":"og:description","content":"科学上网 说明 AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍? 本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。 购买指南 点击进入服务页面, 看到如下页面:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Tool"}],["meta",{"property":"article:published_time","content":"2022-06-20T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"科学上网\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-20T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"说明","slug":"说明","link":"#说明","children":[]},{"level":2,"title":"购买指南","slug":"购买指南","link":"#购买指南","children":[]},{"level":2,"title":"客户端","slug":"客户端","link":"#客户端","children":[]},{"level":2,"title":"导入配置","slug":"导入配置","link":"#导入配置","children":[]},{"level":2,"title":"其他","slug":"其他","link":"#其他","children":[{"level":3,"title":"500 内部代理错误","slug":"_500-内部代理错误","link":"#_500-内部代理错误","children":[]},{"level":3,"title":"修改PAC文件","slug":"修改pac文件","link":"#修改pac文件","children":[]},{"level":3,"title":"使用 New Bing","slug":"使用-new-bing","link":"#使用-new-bing","children":[]},{"level":3,"title":"申请美区apple id","slug":"申请美区apple-id","link":"#申请美区apple-id","children":[]}]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.72,"words":815},"filePathRelative":"tools/how-to-connect-to-internet.md","localizedDate":"2022年6月20日","excerpt":"

科学上网

\\n

说明

\\n

AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍?

\\n

本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。

\\n

购买指南

\\n

点击进入服务页面, 看到如下页面:
\\n\\"image.png\\"

","autoDesc":true}');export{e as data}; diff --git a/assets/how-to-connect-to-internet.html-994bb0e3.js b/assets/how-to-connect-to-internet.html-b65ec9df.js similarity index 98% rename from assets/how-to-connect-to-internet.html-994bb0e3.js rename to assets/how-to-connect-to-internet.html-b65ec9df.js index c25a4aff..cec09210 100644 --- a/assets/how-to-connect-to-internet.html-994bb0e3.js +++ b/assets/how-to-connect-to-internet.html-b65ec9df.js @@ -1,4 +1,4 @@ -import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as s,c as l,a,b as e,d as r,f as i}from"./app-b649ee34.js";const d={},p=i('

科学上网

说明

AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍?

本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。

购买指南

',5),c={href:"https://cp.cloudnx.cc/aff.php?aff=22930",target:"_blank",rel:"noopener noreferrer"},g=a("br",null,null,-1),h=a("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/tools/1682172119852.png",alt:"image.png",loading:"lazy"},null,-1),m=i('

点击注册账户, 会出现如图提示:
image.png

点击购买产品,就会出现查看产品列表面(为什么要这么绕,因为这是保护措施,避免产品主页被黑)。

推荐购买 Basic 套餐,一个人用的话,学习工作、娱乐看视频,每月 50GB 足够了。多个客户端可以同时在线,反正就是随便玩!算下来,一天不到7毛钱,很划算了。
image.png

点击购买后,选择包年,即可享受优惠价格,如下图所示:
image.png

支持支付宝,购买非常方便。

客户端

支持全平台客户端:

',7),u={href:"https://github.com/yichengchen/clashX/releases",target:"_blank",rel:"noopener noreferrer"},b={href:"https://github.com/shadowsocks/shadowsocks-windows/releases",target:"_blank",rel:"noopener noreferrer"},_=a("li",null,"Android 推荐 V2ray",-1),y=a("li",null,"iOS 推荐 Shadowrocket(花点小钱,使用美区 apple id——文末有分享如何申请)",-1),v=i(`

导入配置

以 Shadowsocks 为例。

根据客户端复制相应的订阅地址:
image.png

先禁用系统代理:
image.png

点击在线配置:
image.png

输入URL,点击更新:
image.png

选择一个服务器:
image.png
image.png

再恢复系统代理,选择PAC模式:
image.png

就可以愉快地上网啦!

其他

500 内部代理错误

image.png
image.png

出现此问题时,一般是内网自定义域名不允许走代理,需要禁用掉系统代理:
image.png

修改PAC文件

PAC模式是指:根据规则识别某网站是否需要使用代理访问。

什么时候需要修改PAC文件呢?

  • 当某个网站不想走代理
  • 设置某网站一定走代理

操作如下(以Shadowsocks为例):

按下图所示,模仿添加,即可实现遇到下列网站时选择直连:

不走代理的示例修改:

 "@@||company.yuque.com",
+import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as s,c as l,a,b as e,d as r,f as i}from"./app-a9d55428.js";const d={},p=i('

科学上网

说明

AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍?

本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。

购买指南

',5),c={href:"https://cp.cloudnx.cc/aff.php?aff=22930",target:"_blank",rel:"noopener noreferrer"},g=a("br",null,null,-1),h=a("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/tools/1682172119852.png",alt:"image.png",loading:"lazy"},null,-1),m=i('

点击注册账户, 会出现如图提示:
image.png

点击购买产品,就会出现查看产品列表面(为什么要这么绕,因为这是保护措施,避免产品主页被黑)。

推荐购买 Basic 套餐,一个人用的话,学习工作、娱乐看视频,每月 50GB 足够了。多个客户端可以同时在线,反正就是随便玩!算下来,一天不到7毛钱,很划算了。
image.png

点击购买后,选择包年,即可享受优惠价格,如下图所示:
image.png

支持支付宝,购买非常方便。

客户端

支持全平台客户端:

',7),u={href:"https://github.com/yichengchen/clashX/releases",target:"_blank",rel:"noopener noreferrer"},b={href:"https://github.com/shadowsocks/shadowsocks-windows/releases",target:"_blank",rel:"noopener noreferrer"},_=a("li",null,"Android 推荐 V2ray",-1),y=a("li",null,"iOS 推荐 Shadowrocket(花点小钱,使用美区 apple id——文末有分享如何申请)",-1),v=i(`

导入配置

以 Shadowsocks 为例。

根据客户端复制相应的订阅地址:
image.png

先禁用系统代理:
image.png

点击在线配置:
image.png

输入URL,点击更新:
image.png

选择一个服务器:
image.png
image.png

再恢复系统代理,选择PAC模式:
image.png

就可以愉快地上网啦!

其他

500 内部代理错误

image.png
image.png

出现此问题时,一般是内网自定义域名不允许走代理,需要禁用掉系统代理:
image.png

修改PAC文件

PAC模式是指:根据规则识别某网站是否需要使用代理访问。

什么时候需要修改PAC文件呢?

  • 当某个网站不想走代理
  • 设置某网站一定走代理

操作如下(以Shadowsocks为例):

按下图所示,模仿添加,即可实现遇到下列网站时选择直连:

不走代理的示例修改:

 "@@||company.yuque.com",
 

必走代理的示例修改:

"*.openai.com",
 "*.bing.com",
 

保存后记得重启软件。

使用 New Bing

再给一个配置 Clash 使用 New Bing 的示例:
image.png
编辑文件:
image.png
添加如下设置:

 - DOMAIN-KEYWORD,bing,🚀 节点选择
diff --git a/assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-77c8640b.js b/assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-bbf44529.js
similarity index 98%
rename from assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-77c8640b.js
rename to assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-bbf44529.js
index 4dac7a83..25cd4c7a 100644
--- a/assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-77c8640b.js
+++ b/assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-bbf44529.js
@@ -1,4 +1,4 @@
-import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{o as r,c as i,e as n,a,b as e,f as s}from"./app-b649ee34.js";const o={},d=a("h1",{id:"奇技淫巧-在没有源码的情况下-把-snapshot-转成-release-包",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#奇技淫巧-在没有源码的情况下-把-snapshot-转成-release-包","aria-hidden":"true"},"#"),e(" 奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包")],-1),l=a("h2",{id:"背景",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#背景","aria-hidden":"true"},"#"),e(" 背景")],-1),c=a("p",null,"项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。",-1),m=a("p",null,"问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。",-1),h=s(`

下载


首先我们登录仓库,输入 ArtifactId,找到对应的 snapshot jar 包,并点击进入详情。


找到关键的三个 jar:

  • x.jar
  • x-sources.jar
  • x.pom

分别点击进入详情

依次点击 Path,下载到本地。

修改


首先修改名字,如图所示。

再使用任意工具解压 x.jar,提取出两个文件夹。进入 META-INF


修改 MANIFEST.MF,把里面的 snapshot 字符串去掉(如果有的话)


再点击 maven 文件夹


修改 pom.xml、pom.properties 文件,把里面的 snapshot 字符串去掉。

上传

把前面解压出来的文件重新打包成 jar

jar cvf my-1.4.1.jar com META-INF
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{o as r,c as i,e as n,a,b as e,f as s}from"./app-a9d55428.js";const o={},d=a("h1",{id:"奇技淫巧-在没有源码的情况下-把-snapshot-转成-release-包",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#奇技淫巧-在没有源码的情况下-把-snapshot-转成-release-包","aria-hidden":"true"},"#"),e(" 奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包")],-1),l=a("h2",{id:"背景",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#背景","aria-hidden":"true"},"#"),e(" 背景")],-1),c=a("p",null,"项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。",-1),m=a("p",null,"问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。",-1),h=s(`

下载


首先我们登录仓库,输入 ArtifactId,找到对应的 snapshot jar 包,并点击进入详情。


找到关键的三个 jar:

  • x.jar
  • x-sources.jar
  • x.pom

分别点击进入详情

依次点击 Path,下载到本地。

修改


首先修改名字,如图所示。

再使用任意工具解压 x.jar,提取出两个文件夹。进入 META-INF


修改 MANIFEST.MF,把里面的 snapshot 字符串去掉(如果有的话)


再点击 maven 文件夹


修改 pom.xml、pom.properties 文件,把里面的 snapshot 字符串去掉。

上传

把前面解压出来的文件重新打包成 jar

jar cvf my-1.4.1.jar com META-INF
 
 # you can also use zip 
 # zip -r my-1.4.1.jar com META-INF
diff --git a/assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-66363334.js b/assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-f9386128.js
similarity index 92%
rename from assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-66363334.js
rename to assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-f9386128.js
index 1f4cbdd0..24102f70 100644
--- a/assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-66363334.js
+++ b/assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-f9386128.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-100d1814","path":"/java/how-to-convert-snapshot-into-release-jar-without-source-code.html","title":"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包","lang":"zh-CN","frontmatter":{"date":"2023-08-12T00:00:00.000Z","tag":["Java"],"description":"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包 背景 项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。 问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/how-to-convert-snapshot-into-release-jar-without-source-code.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包"}],["meta",{"property":"og:description","content":"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包 背景 项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。 问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:published_time","content":"2023-08-12T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-12T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"下载","slug":"下载","link":"#下载","children":[]},{"level":2,"title":"修改","slug":"修改","link":"#修改","children":[]},{"level":2,"title":"上传","slug":"上传","link":"#上传","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.41,"words":422},"filePathRelative":"java/how-to-convert-snapshot-into-release-jar-without-source-code.md","localizedDate":"2023年8月12日","excerpt":"

奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包

\\n

背景

\\n

项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。

\\n

问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-100d1814","path":"/java/how-to-convert-snapshot-into-release-jar-without-source-code.html","title":"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包","lang":"zh-CN","frontmatter":{"date":"2023-08-12T00:00:00.000Z","tag":["Java"],"description":"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包 背景 项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。 问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/how-to-convert-snapshot-into-release-jar-without-source-code.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包"}],["meta",{"property":"og:description","content":"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包 背景 项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。 问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:published_time","content":"2023-08-12T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-12T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"下载","slug":"下载","link":"#下载","children":[]},{"level":2,"title":"修改","slug":"修改","link":"#修改","children":[]},{"level":2,"title":"上传","slug":"上传","link":"#上传","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.41,"words":422},"filePathRelative":"java/how-to-convert-snapshot-into-release-jar-without-source-code.md","localizedDate":"2023年8月12日","excerpt":"

奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包

\\n

背景

\\n

项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。

\\n

问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/how-to-self-evaluate-english-level.html-bc3d8f29.js b/assets/how-to-self-evaluate-english-level.html-16ae55da.js similarity index 97% rename from assets/how-to-self-evaluate-english-level.html-bc3d8f29.js rename to assets/how-to-self-evaluate-english-level.html-16ae55da.js index dce5aa68..c6b52a0c 100644 --- a/assets/how-to-self-evaluate-english-level.html-bc3d8f29.js +++ b/assets/how-to-self-evaluate-english-level.html-16ae55da.js @@ -1 +1 @@ -import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c as g,a as e,b as r,d as o,f as i}from"./app-b649ee34.js";const s={},h=i('

英文能力评测手把手教学

前言

之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。

单词量

',4),c={href:"https://preply.com/en/learn/english/test-your-vocab",target:"_blank",rel:"noopener noreferrer"},m=i('

看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。
image.png
在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。
image.png
对于该结果,网站有以下值得注意的解释:

  1. 结果偏差在正负10%左右:

Your vocabulary count has a margin of error of approximately ±10%. We also round results above 10,000 to the nearest 100, and results above 300 to the nearest 10.

  1. 英语非母语者,用单词量作为能力标准通常划分如下:
image.png
image.png
  1. 结果一年测一次最有效
image.png
image.png

阅读能力

',8),p={href:"https://readtheory.org/",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,[r("右上角点击注册:"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682426663590.png",alt:"image.png",loading:"lazy"})],-1),u=e("p",null,[r("选择作为学生:"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682426667189.png",alt:"image.png",loading:"lazy"})],-1),_=e("br",null,null,-1),y=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682426672418.png",alt:"image.png",loading:"lazy"},null,-1),b=e("br",null,null,-1),f={href:"https://www.yuque.com/levy/blog/how-to-surf",target:"_blank",rel:"noopener noreferrer"},v=e("br",null,null,-1),w=i('

确保自己至少有20分钟的空闲时间,之后点击已经准备好了:
image.png

开始做题,一共8道,看不懂就乱选,后面的题目会根据你的答题情况自动调整:
image.png

做完后,点击确认:
image.png

点击下图红框处:
image.png

往下翻阅,即可看到自己的蓝思值初步评分:
image.png

再给出通用的蓝思值能力对照表:
image.png
以上就是今天的英文能力评测手把手教学啦,希望对大家有所帮助。

',6);function x(z,k){const a=t("ExternalLinkIcon");return n(),g("div",null,[h,e("p",null,[r("进入网站:"),e("a",c,[r("https://preply.com/en/learn/english/test-your-vocab"),o(a)])]),m,e("p",null,[r("评估阅读能力也就是评估蓝思值。进入网站:"),e("a",p,[r("https://readtheory.org/"),o(a)])]),d,u,e("p",null,[r("可以直接使用谷歌账号注册:"),_,y,b,r(" 如果不能谷歌,可以看我写的谷歌教程:"),e("a",f,[r("https://www.yuque.com/levy/blog/how-to-surf"),o(a)]),v,r(" 或者自己填表单,进入下一步。")]),w])}const N=l(s,[["render",x],["__file","how-to-self-evaluate-english-level.html.vue"]]);export{N as default}; +import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c as g,a as e,b as r,d as o,f as i}from"./app-a9d55428.js";const s={},h=i('

英文能力评测手把手教学

前言

之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。

单词量

',4),c={href:"https://preply.com/en/learn/english/test-your-vocab",target:"_blank",rel:"noopener noreferrer"},m=i('

看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。
image.png
在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。
image.png
对于该结果,网站有以下值得注意的解释:

  1. 结果偏差在正负10%左右:

Your vocabulary count has a margin of error of approximately ±10%. We also round results above 10,000 to the nearest 100, and results above 300 to the nearest 10.

  1. 英语非母语者,用单词量作为能力标准通常划分如下:
image.png
image.png
  1. 结果一年测一次最有效
image.png
image.png

阅读能力

',8),p={href:"https://readtheory.org/",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,[r("右上角点击注册:"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682426663590.png",alt:"image.png",loading:"lazy"})],-1),u=e("p",null,[r("选择作为学生:"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682426667189.png",alt:"image.png",loading:"lazy"})],-1),_=e("br",null,null,-1),y=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682426672418.png",alt:"image.png",loading:"lazy"},null,-1),b=e("br",null,null,-1),f={href:"https://www.yuque.com/levy/blog/how-to-surf",target:"_blank",rel:"noopener noreferrer"},v=e("br",null,null,-1),w=i('

确保自己至少有20分钟的空闲时间,之后点击已经准备好了:
image.png

开始做题,一共8道,看不懂就乱选,后面的题目会根据你的答题情况自动调整:
image.png

做完后,点击确认:
image.png

点击下图红框处:
image.png

往下翻阅,即可看到自己的蓝思值初步评分:
image.png

再给出通用的蓝思值能力对照表:
image.png
以上就是今天的英文能力评测手把手教学啦,希望对大家有所帮助。

',6);function x(z,k){const a=t("ExternalLinkIcon");return n(),g("div",null,[h,e("p",null,[r("进入网站:"),e("a",c,[r("https://preply.com/en/learn/english/test-your-vocab"),o(a)])]),m,e("p",null,[r("评估阅读能力也就是评估蓝思值。进入网站:"),e("a",p,[r("https://readtheory.org/"),o(a)])]),d,u,e("p",null,[r("可以直接使用谷歌账号注册:"),_,y,b,r(" 如果不能谷歌,可以看我写的谷歌教程:"),e("a",f,[r("https://www.yuque.com/levy/blog/how-to-surf"),o(a)]),v,r(" 或者自己填表单,进入下一步。")]),w])}const N=l(s,[["render",x],["__file","how-to-self-evaluate-english-level.html.vue"]]);export{N as default}; diff --git a/assets/how-to-self-evaluate-english-level.html-61f99657.js b/assets/how-to-self-evaluate-english-level.html-cc5748d1.js similarity index 93% rename from assets/how-to-self-evaluate-english-level.html-61f99657.js rename to assets/how-to-self-evaluate-english-level.html-cc5748d1.js index ad44cf02..0e741c66 100644 --- a/assets/how-to-self-evaluate-english-level.html-61f99657.js +++ b/assets/how-to-self-evaluate-english-level.html-cc5748d1.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-d42db13c","path":"/english/how-to-self-evaluate-english-level.html","title":"英文能力评测手把手教学","lang":"zh-CN","frontmatter":{"date":"2022-07-09T00:00:00.000Z","tag":"English","description":"英文能力评测手把手教学 前言 之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。 单词量 进入网站:https://preply.com/en/learn/english/test-your-vocab 看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。 在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。 对于该结果,网站有以下值得注意的解释:","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/how-to-self-evaluate-english-level.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"英文能力评测手把手教学"}],["meta",{"property":"og:description","content":"英文能力评测手把手教学 前言 之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。 单词量 进入网站:https://preply.com/en/learn/english/test-your-vocab 看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。 在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。 对于该结果,网站有以下值得注意的解释:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-07-09T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"英文能力评测手把手教学\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-07-09T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"单词量","slug":"单词量","link":"#单词量","children":[]},{"level":2,"title":"阅读能力","slug":"阅读能力","link":"#阅读能力","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.85,"words":554},"filePathRelative":"english/how-to-self-evaluate-english-level.md","localizedDate":"2022年7月9日","excerpt":"

英文能力评测手把手教学

\\n

前言

\\n

之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。

\\n

单词量

\\n

进入网站:https://preply.com/en/learn/english/test-your-vocab

\\n

看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。
\\n\\"image.png\\"
\\n在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。
\\n\\"image.png\\"
\\n对于该结果,网站有以下值得注意的解释:

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-d42db13c","path":"/english/how-to-self-evaluate-english-level.html","title":"英文能力评测手把手教学","lang":"zh-CN","frontmatter":{"date":"2022-07-09T00:00:00.000Z","tag":"English","description":"英文能力评测手把手教学 前言 之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。 单词量 进入网站:https://preply.com/en/learn/english/test-your-vocab 看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。 在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。 对于该结果,网站有以下值得注意的解释:","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/how-to-self-evaluate-english-level.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"英文能力评测手把手教学"}],["meta",{"property":"og:description","content":"英文能力评测手把手教学 前言 之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。 单词量 进入网站:https://preply.com/en/learn/english/test-your-vocab 看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。 在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。 对于该结果,网站有以下值得注意的解释:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-07-09T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"英文能力评测手把手教学\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-07-09T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"单词量","slug":"单词量","link":"#单词量","children":[]},{"level":2,"title":"阅读能力","slug":"阅读能力","link":"#阅读能力","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.85,"words":554},"filePathRelative":"english/how-to-self-evaluate-english-level.md","localizedDate":"2022年7月9日","excerpt":"

英文能力评测手把手教学

\\n

前言

\\n

之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。

\\n

单词量

\\n

进入网站:https://preply.com/en/learn/english/test-your-vocab

\\n

看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。
\\n\\"image.png\\"
\\n在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。
\\n\\"image.png\\"
\\n对于该结果,网站有以下值得注意的解释:

","autoDesc":true}');export{e as data}; diff --git a/assets/index.html-0f5c0f0d.js b/assets/index.html-070b1600.js similarity index 71% rename from assets/index.html-0f5c0f0d.js rename to assets/index.html-070b1600.js index e5dcc532..01b4ed37 100644 --- a/assets/index.html-0f5c0f0d.js +++ b/assets/index.html-070b1600.js @@ -1 +1 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-1d3ab01e.js b/assets/index.html-112bc49c.js similarity index 76% rename from assets/index.html-1d3ab01e.js rename to assets/index.html-112bc49c.js index 0058369a..7d1138a0 100644 --- a/assets/index.html-1d3ab01e.js +++ b/assets/index.html-112bc49c.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-b649ee34.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-a9d55428.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-049bd54f.js b/assets/index.html-14879732.js similarity index 71% rename from assets/index.html-049bd54f.js rename to assets/index.html-14879732.js index e5dcc532..01b4ed37 100644 --- a/assets/index.html-049bd54f.js +++ b/assets/index.html-14879732.js @@ -1 +1 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-18a625f7.js b/assets/index.html-18a625f7.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-18a625f7.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-1d219a24.js b/assets/index.html-1d219a24.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-1d219a24.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-2728cdd7.js b/assets/index.html-2728cdd7.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-2728cdd7.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-05a6aceb.js b/assets/index.html-2a568803.js similarity index 71% rename from assets/index.html-05a6aceb.js rename to assets/index.html-2a568803.js index e5dcc532..01b4ed37 100644 --- a/assets/index.html-05a6aceb.js +++ b/assets/index.html-2a568803.js @@ -1 +1 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-342e7095.js b/assets/index.html-342e7095.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-342e7095.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-06bf462d.js b/assets/index.html-3a00d6ef.js similarity index 71% rename from assets/index.html-06bf462d.js rename to assets/index.html-3a00d6ef.js index e5dcc532..01b4ed37 100644 --- a/assets/index.html-06bf462d.js +++ b/assets/index.html-3a00d6ef.js @@ -1 +1 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-3a97ce45.js b/assets/index.html-3a97ce45.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-3a97ce45.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-5a646c34.js b/assets/index.html-408ce1a1.js similarity index 76% rename from assets/index.html-5a646c34.js rename to assets/index.html-408ce1a1.js index 0058369a..7d1138a0 100644 --- a/assets/index.html-5a646c34.js +++ b/assets/index.html-408ce1a1.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-b649ee34.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-a9d55428.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-43e4e5f7.js b/assets/index.html-43e4e5f7.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-43e4e5f7.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-4458b0dc.js b/assets/index.html-4458b0dc.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-4458b0dc.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-6d6c9ccc.js b/assets/index.html-4c220eec.js similarity index 76% rename from assets/index.html-6d6c9ccc.js rename to assets/index.html-4c220eec.js index 0058369a..7d1138a0 100644 --- a/assets/index.html-6d6c9ccc.js +++ b/assets/index.html-4c220eec.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-b649ee34.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-a9d55428.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-4d16e2b0.js b/assets/index.html-4d16e2b0.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-4d16e2b0.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-515fd40f.js b/assets/index.html-515fd40f.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-515fd40f.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-55ab9a01.js b/assets/index.html-55ab9a01.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-55ab9a01.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-55bb043c.js b/assets/index.html-55bb043c.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-55bb043c.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-5859e69f.js b/assets/index.html-5859e69f.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-5859e69f.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-6635a236.js b/assets/index.html-6635a236.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-6635a236.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-6cb930a5.js b/assets/index.html-6cb930a5.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-6cb930a5.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-7ad6c908.js b/assets/index.html-7ad6c908.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-7ad6c908.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-88e3a8c0.js b/assets/index.html-88e3a8c0.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-88e3a8c0.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-896d34f9.js b/assets/index.html-896d34f9.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-896d34f9.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-8be4c9cb.js b/assets/index.html-8be4c9cb.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-8be4c9cb.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-8bec8f40.js b/assets/index.html-8bec8f40.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-8bec8f40.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-9070516e.js b/assets/index.html-9070516e.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-9070516e.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-9122fca1.js b/assets/index.html-9122fca1.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-9122fca1.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-91f24fc6.js b/assets/index.html-91f24fc6.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-91f24fc6.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-956353ed.js b/assets/index.html-956353ed.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-956353ed.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-967c662b.js b/assets/index.html-967c662b.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-967c662b.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-9890dac2.js b/assets/index.html-9890dac2.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-9890dac2.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-98c939cd.js b/assets/index.html-98c939cd.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-98c939cd.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-9b7d49c9.js b/assets/index.html-9b7d49c9.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-9b7d49c9.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-396a0c75.js b/assets/index.html-9c477b31.js similarity index 68% rename from assets/index.html-396a0c75.js rename to assets/index.html-9c477b31.js index a0c1ba01..f5136b2a 100644 --- a/assets/index.html-396a0c75.js +++ b/assets/index.html-9c477b31.js @@ -1 +1 @@ -const e=JSON.parse(`{"key":"v-8daa1a0e","path":"/","title":"levy's blog","lang":"zh-CN","frontmatter":{"home":true,"layout":"BlogHome","icon":"home","title":"levy's blog","heroText":"levy's blog","heroFullScreen":true,"tagline":"思考,表达,练习,创造","description":"","head":[["meta",{"property":"og:url","content":"https://levy.vip/"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"levy's blog"}],["meta",{"property":"og:type","content":"website"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"WebPage\\",\\"name\\":\\"levy's blog\\"}"]]},"headers":[],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.21,"words":64},"filePathRelative":"README.md","localizedDate":"2023年10月26日","excerpt":"","autoDesc":true}`);export{e as data}; +const e=JSON.parse(`{"key":"v-8daa1a0e","path":"/","title":"levy's blog","lang":"zh-CN","frontmatter":{"home":true,"layout":"BlogHome","icon":"home","title":"levy's blog","heroText":"levy's blog","heroFullScreen":true,"tagline":"思考,表达,练习,创造","description":"","head":[["meta",{"property":"og:url","content":"https://levy.vip/"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"levy's blog"}],["meta",{"property":"og:type","content":"website"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"WebPage\\",\\"name\\":\\"levy's blog\\"}"]]},"headers":[],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.21,"words":64},"filePathRelative":"README.md","localizedDate":"2023年11月22日","excerpt":"","autoDesc":true}`);export{e as data}; diff --git a/assets/index.html-86d779f8.js b/assets/index.html-9cbf5acb.js similarity index 76% rename from assets/index.html-86d779f8.js rename to assets/index.html-9cbf5acb.js index 0058369a..7d1138a0 100644 --- a/assets/index.html-86d779f8.js +++ b/assets/index.html-9cbf5acb.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-b649ee34.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-a9d55428.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-9fa06e1f.js b/assets/index.html-9fa06e1f.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-9fa06e1f.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-a0b725d1.js b/assets/index.html-a0b725d1.js deleted file mode 100644 index 0058369a..00000000 --- a/assets/index.html-a0b725d1.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-b649ee34.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-a621f81c.js b/assets/index.html-a621f81c.js new file mode 100644 index 00000000..7d1138a0 --- /dev/null +++ b/assets/index.html-a621f81c.js @@ -0,0 +1 @@ +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-a9d55428.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-a6b0d770.js b/assets/index.html-a6b0d770.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-a6b0d770.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-ae84f935.js b/assets/index.html-ae84f935.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-ae84f935.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-b4618361.js b/assets/index.html-b4618361.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-b4618361.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-b5310c3c.js b/assets/index.html-b5310c3c.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-b5310c3c.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-b69b0952.js b/assets/index.html-b69b0952.js deleted file mode 100644 index 0058369a..00000000 --- a/assets/index.html-b69b0952.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-b649ee34.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-b895087a.js b/assets/index.html-b895087a.js new file mode 100644 index 00000000..7d1138a0 --- /dev/null +++ b/assets/index.html-b895087a.js @@ -0,0 +1 @@ +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-a9d55428.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-b9ef174d.js b/assets/index.html-b9ef174d.js deleted file mode 100644 index 0058369a..00000000 --- a/assets/index.html-b9ef174d.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-b649ee34.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-bd2fe08a.js b/assets/index.html-bd2fe08a.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-bd2fe08a.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-bdcc3d36.js b/assets/index.html-bdcc3d36.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-bdcc3d36.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-bfd1105e.js b/assets/index.html-bfd1105e.js new file mode 100644 index 00000000..7d1138a0 --- /dev/null +++ b/assets/index.html-bfd1105e.js @@ -0,0 +1 @@ +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-a9d55428.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-c6b00465.js b/assets/index.html-c6b00465.js new file mode 100644 index 00000000..7d1138a0 --- /dev/null +++ b/assets/index.html-c6b00465.js @@ -0,0 +1 @@ +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-a9d55428.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-c933b166.js b/assets/index.html-c933b166.js new file mode 100644 index 00000000..7d1138a0 --- /dev/null +++ b/assets/index.html-c933b166.js @@ -0,0 +1 @@ +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-a9d55428.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-d70e9d37.js b/assets/index.html-d70e9d37.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-d70e9d37.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-e013b2c1.js b/assets/index.html-e013b2c1.js deleted file mode 100644 index 0058369a..00000000 --- a/assets/index.html-e013b2c1.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-b649ee34.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-e53262f0.js b/assets/index.html-e53262f0.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-e53262f0.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-e76096f8.js b/assets/index.html-e76096f8.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-e76096f8.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-e7a8926e.js b/assets/index.html-e7a8926e.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-e7a8926e.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-e9e56fc9.js b/assets/index.html-e9e56fc9.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-e9e56fc9.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-f1f01e82.js b/assets/index.html-f1f01e82.js deleted file mode 100644 index 0058369a..00000000 --- a/assets/index.html-f1f01e82.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-b649ee34.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-f4939e44.js b/assets/index.html-f4939e44.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-f4939e44.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-fb514775.js b/assets/index.html-fb514775.js new file mode 100644 index 00000000..01b4ed37 --- /dev/null +++ b/assets/index.html-fb514775.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-a9d55428.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-feab3d86.js b/assets/index.html-feab3d86.js deleted file mode 100644 index e5dcc532..00000000 --- a/assets/index.html-feab3d86.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-b649ee34.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/iteration-retrospective-of-sanyuan.html-27cf4da2.js b/assets/iteration-retrospective-of-sanyuan.html-8d424c1c.js similarity index 98% rename from assets/iteration-retrospective-of-sanyuan.html-27cf4da2.js rename to assets/iteration-retrospective-of-sanyuan.html-8d424c1c.js index e324e0bc..34a3c81a 100644 --- a/assets/iteration-retrospective-of-sanyuan.html-27cf4da2.js +++ b/assets/iteration-retrospective-of-sanyuan.html-8d424c1c.js @@ -1 +1 @@ -import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c as i,e as o,a as e,b as a,f as n}from"./app-b649ee34.js";const d={},s=e("h1",{id:"迭代复盘之三员管理",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#迭代复盘之三员管理","aria-hidden":"true"},"#"),a(" 迭代复盘之三员管理")],-1),c=e("h2",{id:"前言",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),a(" 前言")],-1),l=e("p",null,"本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。",-1),h=e("p",null,"这次迭代因为各种原因,延期了快一个星期(周六还加了班)。",-1),p=e("p",null,"那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。",-1),_=n('

动手前,至少梳理出接口清单

太轻信旧代码了,抱着代码迁移过来,跑起来就能用的态度,没能及时发现功能遗漏点,到了提测才发现有功能未实现,这是可以避免的。

更优的工作流应该是:

  1. 先进行业务梳理
  2. 整理出接口清单
  3. 再进行代码迁移
  4. 根据接口清单,逐个验证迁移的代码是否符合需求

迁移时,采取结队编程

看到有 500 个文件要迁移,觉得用分治的思路去实现会更高效,但这种思维有一个误区:做得快不等于有效率,因为效率需要的是有用功,而不是无用功。

实践表明,初期对业务的理解、代码的熟悉程度还不够深入,迁移代码遇到分歧点时,个人在独自处理时容易产生错误——而这种错误并不自知,这才是最蛋疼的地方。

所以,我才建议,采取结队编程实践,两个人一起来迁移代码,这样子遇到分歧时可以讨论,而不是贸然行动。表面上看,这样的进程慢了,实际上却能提高对代码、业务的理解,提高了迁移的正确率,真正地提高效率。

迁移后,需要对自己负责的功能设计测试用例

这里的本质,是不能完全依赖别人。虽然测试人员会编写测试用例,也会进行用例评审,但这里会有两个问题:

  1. 前期评审时,自己的心思可能在理解业务、功能设计与改造以及代码迁移,没有多少心思能去 debug 用例的精细程度
  2. 测试也是人,也会有遗漏,并且没有接触源码,有些特殊的、极端的边界场景,自己作为研发人员,更容易发现

可以看成,我们自己设计的用例是对测试人员给出的测试用例的补充。

当然,由于时间的关系,做完善的测试一般都不太可能了。
对于 legacy code,想补充 service 层的单元测试成本是很高的。写 controller 层的测试,可能也没有太多时间。
但至少应该用 postman 进行调试,或者在用户界面上点几下,不能完全等着前端或测试人员反馈,认为没有反馈就是没有 bug。

',13);function f(u,m){return t(),i("div",null,[s,c,l,h,p,o(" more "),_])}const v=r(d,[["render",f],["__file","iteration-retrospective-of-sanyuan.html.vue"]]);export{v as default}; +import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c as i,e as o,a as e,b as a,f as n}from"./app-a9d55428.js";const d={},s=e("h1",{id:"迭代复盘之三员管理",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#迭代复盘之三员管理","aria-hidden":"true"},"#"),a(" 迭代复盘之三员管理")],-1),c=e("h2",{id:"前言",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),a(" 前言")],-1),l=e("p",null,"本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。",-1),h=e("p",null,"这次迭代因为各种原因,延期了快一个星期(周六还加了班)。",-1),p=e("p",null,"那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。",-1),_=n('

动手前,至少梳理出接口清单

太轻信旧代码了,抱着代码迁移过来,跑起来就能用的态度,没能及时发现功能遗漏点,到了提测才发现有功能未实现,这是可以避免的。

更优的工作流应该是:

  1. 先进行业务梳理
  2. 整理出接口清单
  3. 再进行代码迁移
  4. 根据接口清单,逐个验证迁移的代码是否符合需求

迁移时,采取结队编程

看到有 500 个文件要迁移,觉得用分治的思路去实现会更高效,但这种思维有一个误区:做得快不等于有效率,因为效率需要的是有用功,而不是无用功。

实践表明,初期对业务的理解、代码的熟悉程度还不够深入,迁移代码遇到分歧点时,个人在独自处理时容易产生错误——而这种错误并不自知,这才是最蛋疼的地方。

所以,我才建议,采取结队编程实践,两个人一起来迁移代码,这样子遇到分歧时可以讨论,而不是贸然行动。表面上看,这样的进程慢了,实际上却能提高对代码、业务的理解,提高了迁移的正确率,真正地提高效率。

迁移后,需要对自己负责的功能设计测试用例

这里的本质,是不能完全依赖别人。虽然测试人员会编写测试用例,也会进行用例评审,但这里会有两个问题:

  1. 前期评审时,自己的心思可能在理解业务、功能设计与改造以及代码迁移,没有多少心思能去 debug 用例的精细程度
  2. 测试也是人,也会有遗漏,并且没有接触源码,有些特殊的、极端的边界场景,自己作为研发人员,更容易发现

可以看成,我们自己设计的用例是对测试人员给出的测试用例的补充。

当然,由于时间的关系,做完善的测试一般都不太可能了。
对于 legacy code,想补充 service 层的单元测试成本是很高的。写 controller 层的测试,可能也没有太多时间。
但至少应该用 postman 进行调试,或者在用户界面上点几下,不能完全等着前端或测试人员反馈,认为没有反馈就是没有 bug。

',13);function f(u,m){return t(),i("div",null,[s,c,l,h,p,o(" more "),_])}const v=r(d,[["render",f],["__file","iteration-retrospective-of-sanyuan.html.vue"]]);export{v as default}; diff --git a/assets/iteration-retrospective-of-sanyuan.html-9a8c4533.js b/assets/iteration-retrospective-of-sanyuan.html-b44a88d2.js similarity index 89% rename from assets/iteration-retrospective-of-sanyuan.html-9a8c4533.js rename to assets/iteration-retrospective-of-sanyuan.html-b44a88d2.js index 23696623..053c2e9d 100644 --- a/assets/iteration-retrospective-of-sanyuan.html-9a8c4533.js +++ b/assets/iteration-retrospective-of-sanyuan.html-b44a88d2.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-fd7b7f92","path":"/daily/iteration-retrospective-of-sanyuan.html","title":"迭代复盘之三员管理","lang":"zh-CN","frontmatter":{"date":"2023-09-08T00:00:00.000Z","tag":["Daily","Working Experience"],"description":"迭代复盘之三员管理 前言 本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。 这次迭代因为各种原因,延期了快一个星期(周六还加了班)。 那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/iteration-retrospective-of-sanyuan.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"迭代复盘之三员管理"}],["meta",{"property":"og:description","content":"迭代复盘之三员管理 前言 本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。 这次迭代因为各种原因,延期了快一个星期(周六还加了班)。 那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Working Experience"}],["meta",{"property":"article:published_time","content":"2023-09-08T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"迭代复盘之三员管理\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-08T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"动手前,至少梳理出接口清单","slug":"动手前-至少梳理出接口清单","link":"#动手前-至少梳理出接口清单","children":[]},{"level":2,"title":"迁移时,采取结队编程","slug":"迁移时-采取结队编程","link":"#迁移时-采取结队编程","children":[]},{"level":2,"title":"迁移后,需要对自己负责的功能设计测试用例","slug":"迁移后-需要对自己负责的功能设计测试用例","link":"#迁移后-需要对自己负责的功能设计测试用例","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.52,"words":756},"filePathRelative":"daily/iteration-retrospective-of-sanyuan.md","localizedDate":"2023年9月8日","excerpt":"

迭代复盘之三员管理

\\n

前言

\\n

本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。

\\n

这次迭代因为各种原因,延期了快一个星期(周六还加了班)。

\\n

那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-fd7b7f92","path":"/daily/iteration-retrospective-of-sanyuan.html","title":"迭代复盘之三员管理","lang":"zh-CN","frontmatter":{"date":"2023-09-08T00:00:00.000Z","tag":["Daily","Working Experience"],"description":"迭代复盘之三员管理 前言 本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。 这次迭代因为各种原因,延期了快一个星期(周六还加了班)。 那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/iteration-retrospective-of-sanyuan.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"迭代复盘之三员管理"}],["meta",{"property":"og:description","content":"迭代复盘之三员管理 前言 本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。 这次迭代因为各种原因,延期了快一个星期(周六还加了班)。 那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Working Experience"}],["meta",{"property":"article:published_time","content":"2023-09-08T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"迭代复盘之三员管理\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-08T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"动手前,至少梳理出接口清单","slug":"动手前-至少梳理出接口清单","link":"#动手前-至少梳理出接口清单","children":[]},{"level":2,"title":"迁移时,采取结队编程","slug":"迁移时-采取结队编程","link":"#迁移时-采取结队编程","children":[]},{"level":2,"title":"迁移后,需要对自己负责的功能设计测试用例","slug":"迁移后-需要对自己负责的功能设计测试用例","link":"#迁移后-需要对自己负责的功能设计测试用例","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.52,"words":756},"filePathRelative":"daily/iteration-retrospective-of-sanyuan.md","localizedDate":"2023年9月8日","excerpt":"

迭代复盘之三员管理

\\n

前言

\\n

本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。

\\n

这次迭代因为各种原因,延期了快一个星期(周六还加了班)。

\\n

那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/learning-7000-words-task-completed.html-8232d487.js b/assets/learning-7000-words-task-completed.html-c7c6176d.js similarity index 88% rename from assets/learning-7000-words-task-completed.html-8232d487.js rename to assets/learning-7000-words-task-completed.html-c7c6176d.js index 556f9518..2e115214 100644 --- a/assets/learning-7000-words-task-completed.html-8232d487.js +++ b/assets/learning-7000-words-task-completed.html-c7c6176d.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-6ed7d996","path":"/english/learning-7000-words-task-completed.html","title":"完成刷7k单词任务","lang":"zh-CN","frontmatter":{"date":"2022-09-17T00:00:00.000Z","tag":"English","description":"完成刷7k单词任务 这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。 首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/learning-7000-words-task-completed.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"完成刷7k单词任务"}],["meta",{"property":"og:description","content":"完成刷7k单词任务 这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。 首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-09-17T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"完成刷7k单词任务\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-09-17T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.54,"words":1362},"filePathRelative":"english/learning-7000-words-task-completed.md","localizedDate":"2022年9月17日","excerpt":"

完成刷7k单词任务

\\n

\\"image.png\\"
\\n这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。

\\n

首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-6ed7d996","path":"/english/learning-7000-words-task-completed.html","title":"完成刷7k单词任务","lang":"zh-CN","frontmatter":{"date":"2022-09-17T00:00:00.000Z","tag":"English","description":"完成刷7k单词任务 这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。 首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/learning-7000-words-task-completed.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"完成刷7k单词任务"}],["meta",{"property":"og:description","content":"完成刷7k单词任务 这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。 首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-09-17T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"完成刷7k单词任务\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-09-17T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.54,"words":1362},"filePathRelative":"english/learning-7000-words-task-completed.md","localizedDate":"2022年9月17日","excerpt":"

完成刷7k单词任务

\\n

\\"image.png\\"
\\n这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。

\\n

首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。

","autoDesc":true}');export{e as data}; diff --git a/assets/learning-7000-words-task-completed.html-55872900.js b/assets/learning-7000-words-task-completed.html-f60d77c6.js similarity index 98% rename from assets/learning-7000-words-task-completed.html-55872900.js rename to assets/learning-7000-words-task-completed.html-f60d77c6.js index 00f609ab..55dc9ff3 100644 --- a/assets/learning-7000-words-task-completed.html-55872900.js +++ b/assets/learning-7000-words-task-completed.html-f60d77c6.js @@ -1 +1 @@ -import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{o as i,c as e,f as r}from"./app-b649ee34.js";const p={},a=r('

完成刷7k单词任务

image.png
这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。

首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。

再者,说明下为什么要做这件事。主要原因有三:

  1. 主动扩充词汇量,与英文阅读相辅相成。诚然,可以一边阅读一边积累词汇,但这种方式我认为是“被动”的,并且有看我之前英文阅读经验分享的朋友,会知道我阅读时查词的频率很低的,如此增加词汇量的效率太慢。另外,词汇又有高频与低频、重点与非重点之分,而这是单词本的强项,因此利用单词本是接触“精华词汇”的高效方式。
  2. 我想知道自己确切的词汇量。虽然在之前分享的在线工具中,我评测过自己的词汇量(7000),但自己感觉有点“虚”,一方面误差有10%,另一方面我觉得波动太大了(两年前测是4000),不太相信,于是打算用刷单词本的“笨方法”去确认自己的词汇量。
  3. 我想体验一下刷单词本的经历。这点我在之前的文章中也透露过,我在学生时代是没有买过单词本、也没有拿着单词本去背诵过,基本都是靠音标+词根,边学课文时边记忆,讲究的是一个自然,没有刻意。然而,这隐约有种“不踏实”之感,没有经过刻意练习,似乎得来的单词“不会珍惜”,于是便趁这个机会把这个经历补上。

最后,我想强调的是,注意我“刷单词”,而不是“背单词”这种描述上的差别。“刷”重在强调,过一遍,有个印象、能认识;“背”强调的是死记硬背,机械地记忆——虽然我认为练习需要刻意,但却不认为它等同于死记。

很多人在学习单词觉得很痛苦,半途而废,很大的原因是强调背,把内容强塞进脑子里很让人痛苦;而背了又忘,更是让人感到强烈的挫败感。在这里,我想分享的经验是,此时需要进行思维的转换:你允许自己忘记某些单词。可以把新单词想像成陌生人,总有人能与你成为好友,总有人不能给你留下深刻印象,总有人与你相处不愉快——这么多单词,你先认识容易认识的,很难“相处”、容易忘记的单词,后面再说呗,没必要一口气吃成胖子。

为了减少痛苦,我有以下经验,它们是属于启发式的(heuristic),仅供参考。每个人应总结符合自己的经验,其核心思想是分层次、分类,不要一刀切。

对单词的掌握程度区分优先级:

  • 读:阅读时能看懂是什么意思;就算第一眼看不出单词意思,结合上下文能推测出意思才行
  • 听:别人正常语速、慢速说时,自己能听出这是哪个单词
  • 说:我不对此有强制要求,看着音标能发音即可,因为当前我的英语实践场景中,并不侧重说
  • 写:此优先级最低,因为写作不是考试,我完全可以查词典,因此有些词很难记我就索性不记,如官僚主义(bureaucratism)、资产阶级(bourgeois)——我能听、阅读,但放弃掌握写

对词意进行分类掌握:有的词十几个意思,你哪能一下子全记住?

  • 结合相应的例句,一次只记一个意思
  • 常见的词意,优先记住
  • 冷门的词意,可以跳过
  • 另外,有些中文词意解释很牵强,可以找英英释义

对词汇进行分类掌握:识别出单词属于哪个领域的

  • 计算机、法律、经济、日常生活、文学等领域的词汇,我放在较高优先级,尽量去掌握
  • 医学领域、非考试重点词汇,我不太重视,能认识就认识,觉得认识上有困难就跳过

当然,单词不能纯刷,要结合阅读一起来做。我选择的是外国语学院的英语专业教材《现代大学英语精读》,二者结合,相辅相成。

还有就是,最好固定时间段,以便形成习惯,如上班坐地铁,或晚饭后等较规律的时间段。

最后,我想说的是,刷了这本单词本,并不意味着我就 master 7,000 English words。它对我而言,更多的是在英语学习经历上,具有里程碑的意义。回顾一下,从2022年2月份开始到,到现在历经大约7个月,我算是把英语单词这件事的“遗憾”给补上了,此事至此告一段落,我可以开始新的旅程了🎉。

',17),o=[a];function t(s,n){return i(),e("div",null,o)}const m=l(p,[["render",t],["__file","learning-7000-words-task-completed.html.vue"]]);export{m as default}; +import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{o as i,c as e,f as r}from"./app-a9d55428.js";const p={},a=r('

完成刷7k单词任务

image.png
这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。

首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。

再者,说明下为什么要做这件事。主要原因有三:

  1. 主动扩充词汇量,与英文阅读相辅相成。诚然,可以一边阅读一边积累词汇,但这种方式我认为是“被动”的,并且有看我之前英文阅读经验分享的朋友,会知道我阅读时查词的频率很低的,如此增加词汇量的效率太慢。另外,词汇又有高频与低频、重点与非重点之分,而这是单词本的强项,因此利用单词本是接触“精华词汇”的高效方式。
  2. 我想知道自己确切的词汇量。虽然在之前分享的在线工具中,我评测过自己的词汇量(7000),但自己感觉有点“虚”,一方面误差有10%,另一方面我觉得波动太大了(两年前测是4000),不太相信,于是打算用刷单词本的“笨方法”去确认自己的词汇量。
  3. 我想体验一下刷单词本的经历。这点我在之前的文章中也透露过,我在学生时代是没有买过单词本、也没有拿着单词本去背诵过,基本都是靠音标+词根,边学课文时边记忆,讲究的是一个自然,没有刻意。然而,这隐约有种“不踏实”之感,没有经过刻意练习,似乎得来的单词“不会珍惜”,于是便趁这个机会把这个经历补上。

最后,我想强调的是,注意我“刷单词”,而不是“背单词”这种描述上的差别。“刷”重在强调,过一遍,有个印象、能认识;“背”强调的是死记硬背,机械地记忆——虽然我认为练习需要刻意,但却不认为它等同于死记。

很多人在学习单词觉得很痛苦,半途而废,很大的原因是强调背,把内容强塞进脑子里很让人痛苦;而背了又忘,更是让人感到强烈的挫败感。在这里,我想分享的经验是,此时需要进行思维的转换:你允许自己忘记某些单词。可以把新单词想像成陌生人,总有人能与你成为好友,总有人不能给你留下深刻印象,总有人与你相处不愉快——这么多单词,你先认识容易认识的,很难“相处”、容易忘记的单词,后面再说呗,没必要一口气吃成胖子。

为了减少痛苦,我有以下经验,它们是属于启发式的(heuristic),仅供参考。每个人应总结符合自己的经验,其核心思想是分层次、分类,不要一刀切。

对单词的掌握程度区分优先级:

  • 读:阅读时能看懂是什么意思;就算第一眼看不出单词意思,结合上下文能推测出意思才行
  • 听:别人正常语速、慢速说时,自己能听出这是哪个单词
  • 说:我不对此有强制要求,看着音标能发音即可,因为当前我的英语实践场景中,并不侧重说
  • 写:此优先级最低,因为写作不是考试,我完全可以查词典,因此有些词很难记我就索性不记,如官僚主义(bureaucratism)、资产阶级(bourgeois)——我能听、阅读,但放弃掌握写

对词意进行分类掌握:有的词十几个意思,你哪能一下子全记住?

  • 结合相应的例句,一次只记一个意思
  • 常见的词意,优先记住
  • 冷门的词意,可以跳过
  • 另外,有些中文词意解释很牵强,可以找英英释义

对词汇进行分类掌握:识别出单词属于哪个领域的

  • 计算机、法律、经济、日常生活、文学等领域的词汇,我放在较高优先级,尽量去掌握
  • 医学领域、非考试重点词汇,我不太重视,能认识就认识,觉得认识上有困难就跳过

当然,单词不能纯刷,要结合阅读一起来做。我选择的是外国语学院的英语专业教材《现代大学英语精读》,二者结合,相辅相成。

还有就是,最好固定时间段,以便形成习惯,如上班坐地铁,或晚饭后等较规律的时间段。

最后,我想说的是,刷了这本单词本,并不意味着我就 master 7,000 English words。它对我而言,更多的是在英语学习经历上,具有里程碑的意义。回顾一下,从2022年2月份开始到,到现在历经大约7个月,我算是把英语单词这件事的“遗憾”给补上了,此事至此告一段落,我可以开始新的旅程了🎉。

',17),o=[a];function t(s,n){return i(),e("div",null,o)}const m=l(p,[["render",t],["__file","learning-7000-words-task-completed.html.vue"]]);export{m as default}; diff --git a/assets/let-chatgpt-be-your-foreign-language-teacher.html-3ed89775.js b/assets/let-chatgpt-be-your-foreign-language-teacher.html-18abfed2.js similarity index 94% rename from assets/let-chatgpt-be-your-foreign-language-teacher.html-3ed89775.js rename to assets/let-chatgpt-be-your-foreign-language-teacher.html-18abfed2.js index e19b6ab2..11b3d8d5 100644 --- a/assets/let-chatgpt-be-your-foreign-language-teacher.html-3ed89775.js +++ b/assets/let-chatgpt-be-your-foreign-language-teacher.html-18abfed2.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-221efd1f","path":"/english/let-chatgpt-be-your-foreign-language-teacher.html","title":"让 ChatGPT 成为你的外语私教","lang":"zh-CN","frontmatter":{"date":"2023-05-06T00:00:00.000Z","tag":["English","AI"],"description":"让 ChatGPT 成为你的外语私教 前言 有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。 本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。 准备工作 在开始之前,要准备好几样东西: ChatGPT, 如果没有账号或不能上网,请查看上网教程 Chrome 浏览器插件 voice-control-for-chatgpt 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/let-chatgpt-be-your-foreign-language-teacher.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"让 ChatGPT 成为你的外语私教"}],["meta",{"property":"og:description","content":"让 ChatGPT 成为你的外语私教 前言 有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。 本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。 准备工作 在开始之前,要准备好几样东西: ChatGPT, 如果没有账号或不能上网,请查看上网教程 Chrome 浏览器插件 voice-control-for-chatgpt 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:tag","content":"AI"}],["meta",{"property":"article:published_time","content":"2023-05-06T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"让 ChatGPT 成为你的外语私教\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-05-06T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"准备工作","slug":"准备工作","link":"#准备工作","children":[]},{"level":2,"title":"常用Prompt","slug":"常用prompt","link":"#常用prompt","children":[]},{"level":2,"title":"进行对话","slug":"进行对话","link":"#进行对话","children":[]},{"level":2,"title":"记录回答","slug":"记录回答","link":"#记录回答","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.83,"words":849},"filePathRelative":"english/let-chatgpt-be-your-foreign-language-teacher.md","localizedDate":"2023年5月6日","excerpt":"

让 ChatGPT 成为你的外语私教

\\n

前言

\\n

有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。

\\n

本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。

\\n

准备工作

\\n

在开始之前,要准备好几样东西:

\\n
    \\n
  1. ChatGPT, 如果没有账号或不能上网,请查看上网教程
  2. \\n
  3. Chrome 浏览器插件 voice-control-for-chatgpt
  4. \\n
  5. 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明
  6. \\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-221efd1f","path":"/english/let-chatgpt-be-your-foreign-language-teacher.html","title":"让 ChatGPT 成为你的外语私教","lang":"zh-CN","frontmatter":{"date":"2023-05-06T00:00:00.000Z","tag":["English","AI"],"description":"让 ChatGPT 成为你的外语私教 前言 有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。 本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。 准备工作 在开始之前,要准备好几样东西: ChatGPT, 如果没有账号或不能上网,请查看上网教程 Chrome 浏览器插件 voice-control-for-chatgpt 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/let-chatgpt-be-your-foreign-language-teacher.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"让 ChatGPT 成为你的外语私教"}],["meta",{"property":"og:description","content":"让 ChatGPT 成为你的外语私教 前言 有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。 本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。 准备工作 在开始之前,要准备好几样东西: ChatGPT, 如果没有账号或不能上网,请查看上网教程 Chrome 浏览器插件 voice-control-for-chatgpt 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:tag","content":"AI"}],["meta",{"property":"article:published_time","content":"2023-05-06T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"让 ChatGPT 成为你的外语私教\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-05-06T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"准备工作","slug":"准备工作","link":"#准备工作","children":[]},{"level":2,"title":"常用Prompt","slug":"常用prompt","link":"#常用prompt","children":[]},{"level":2,"title":"进行对话","slug":"进行对话","link":"#进行对话","children":[]},{"level":2,"title":"记录回答","slug":"记录回答","link":"#记录回答","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.83,"words":849},"filePathRelative":"english/let-chatgpt-be-your-foreign-language-teacher.md","localizedDate":"2023年5月6日","excerpt":"

让 ChatGPT 成为你的外语私教

\\n

前言

\\n

有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。

\\n

本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。

\\n

准备工作

\\n

在开始之前,要准备好几样东西:

\\n
    \\n
  1. ChatGPT, 如果没有账号或不能上网,请查看上网教程
  2. \\n
  3. Chrome 浏览器插件 voice-control-for-chatgpt
  4. \\n
  5. 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明
  6. \\n
","autoDesc":true}');export{e as data}; diff --git a/assets/let-chatgpt-be-your-foreign-language-teacher.html-646de3af.js b/assets/let-chatgpt-be-your-foreign-language-teacher.html-4287d21b.js similarity index 99% rename from assets/let-chatgpt-be-your-foreign-language-teacher.html-646de3af.js rename to assets/let-chatgpt-be-your-foreign-language-teacher.html-4287d21b.js index a5228f52..cbbbf89e 100644 --- a/assets/let-chatgpt-be-your-foreign-language-teacher.html-646de3af.js +++ b/assets/let-chatgpt-be-your-foreign-language-teacher.html-4287d21b.js @@ -1,4 +1,4 @@ -import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as s,c as g,a as e,b as a,d as i,w as m,f as t}from"./app-b649ee34.js";const d={},p=t('

让 ChatGPT 成为你的外语私教

前言

有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。

本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。

准备工作

在开始之前,要准备好几样东西:

',6),c={start:"0"},h={href:"https://chat.openai.com/",target:"_blank",rel:"noopener noreferrer"},u={href:"https://chrome.google.com/webstore/detail/voice-control-for-chatgpt/eollffkcakegifhacjnlnegohfdlidhn",target:"_blank",rel:"noopener noreferrer"},_={href:"https://liuxue.koolearn.com/ielts/speak-1-44-0/",target:"_blank",rel:"noopener noreferrer"},b=t(`

安装好插件后,打开 chatGPT 界面,下方就会出现语音输入按钮。
image.png

常用Prompt

下面总结了常用的 Prompt,可以有根据需要进行使用或调整。

设置角色:

  1. Please act as an English teacher.
  2. Please act as an English-speaking test examiner.
  3. Please act as IELTS speaking test examiner.

进入一问一答模式:

  1. You're supposed to asked me questions and wait for my answer. The next question is: xxx

对回答进行完善:

  1. Please revise my answer
  2. Please modify my answer to make it more fluent

对回答进行评分:

  1. Please rate my answer

进行对话

第一句话,是设置好 AI 的角色,让它扮演口语考官。

可以使用以下 prompt:

act as an English-speaking test examiner
+import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as s,c as g,a as e,b as a,d as i,w as m,f as t}from"./app-a9d55428.js";const d={},p=t('

让 ChatGPT 成为你的外语私教

前言

有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。

本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。

准备工作

在开始之前,要准备好几样东西:

',6),c={start:"0"},h={href:"https://chat.openai.com/",target:"_blank",rel:"noopener noreferrer"},u={href:"https://chrome.google.com/webstore/detail/voice-control-for-chatgpt/eollffkcakegifhacjnlnegohfdlidhn",target:"_blank",rel:"noopener noreferrer"},_={href:"https://liuxue.koolearn.com/ielts/speak-1-44-0/",target:"_blank",rel:"noopener noreferrer"},b=t(`

安装好插件后,打开 chatGPT 界面,下方就会出现语音输入按钮。
image.png

常用Prompt

下面总结了常用的 Prompt,可以有根据需要进行使用或调整。

设置角色:

  1. Please act as an English teacher.
  2. Please act as an English-speaking test examiner.
  3. Please act as IELTS speaking test examiner.

进入一问一答模式:

  1. You're supposed to asked me questions and wait for my answer. The next question is: xxx

对回答进行完善:

  1. Please revise my answer
  2. Please modify my answer to make it more fluent

对回答进行评分:

  1. Please rate my answer

进行对话

第一句话,是设置好 AI 的角色,让它扮演口语考官。

可以使用以下 prompt:

act as an English-speaking test examiner
 

image.png
可以看出,语音转文字出现错误,单词 IELTS 始终未能正确识别,但 ChatGPT 却能明白其中的意思。

开启语音插件的意义在于,如果语音识别不了自己说的话,很有可能是自己的发音有问题,起到提醒自己纠正发音的作用。另外,ChatGPT 回复的文字,也会转换成语音输出,顺便练习了听力。

根据练习材料,让 AI 问自己问题。
image.png
记得让 AI 对自己的回答评分,可以使用以下 prompt:

please rate my answer after I answer the question each time
 
image.png
image.png

进行下一个问题:
image.png
上述回答不太好,AI 给出了理由:
image.png

修改后再回答,有所进步
image.png

再问下一个问题:
image.png
这个回答同样不理想,但看了提示也不知道要怎么改:
image.png

此时可以新建一个聊天窗口,让 AI 提供示例回答:
image.png

根据示例答案,结合关键词,重新组织语言,切换回原聊天窗口,再回答一次:
image.png
有所进步!

有时对话长了,AI 会“糊涂”如下所示:
image.png

此时要重新强调它扮演的角色,让其回忆起上下文,可以使用以下 prompt:

focus on the speaking test and assume that you ask me this question
 
image.png
image.png

除此之外,就没啥值得注意的了。重复上述过程,不断练习即可。

记录回答

做完了练习,还要作笔记。但在记录回答之前,还要润色一下,毕竟口语表达的时候,可能会存在语法错误。

`,32),f={href:"https://quillbot.com/",target:"_blank",rel:"noopener noreferrer"},v=e("br",null,null,-1),y=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1683211964246.png",alt:"image.png",loading:"lazy"},null,-1),w=e("br",null,null,-1),k=e("br",null,null,-1),x=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1683211997332.png",alt:"image.png",loading:"lazy"},null,-1),z=e("br",null,null,-1);function P(I,T){const r=n("ExternalLinkIcon"),o=n("RouterLink");return s(),g("div",null,[p,e("ol",c,[e("li",null,[e("a",h,[a("ChatGPT"),i(r)]),a(", 如果没有账号或不能上网,请查看"),i(o,{to:"/tools/how-to-connect-to-internet.html"},{default:m(()=>[a("上网教程")]),_:1})]),e("li",null,[e("a",u,[a("Chrome 浏览器插件 voice-control-for-chatgpt"),i(r)])]),e("li",null,[a("口语练习题,根据个人需求查找即可,下文将以"),e("a",_,[a("雅思"),i(r)]),a("为例进行说明")])]),b,e("p",null,[a("进入 "),e("a",f,[a("https://quillbot.com/"),i(r)]),a(",把答案复制上去,先进行语法检查:"),v,y,w,a(" 再进行流畅度润色:"),k,x,z,a(" 最后保存到笔记本上即可。")])])}const E=l(d,[["render",P],["__file","let-chatgpt-be-your-foreign-language-teacher.html.vue"]]);export{E as default}; diff --git a/assets/leverage-ai-to-boost-coding-productivity.html-fac3783c.js b/assets/leverage-ai-to-boost-coding-productivity.html-0894834e.js similarity index 90% rename from assets/leverage-ai-to-boost-coding-productivity.html-fac3783c.js rename to assets/leverage-ai-to-boost-coding-productivity.html-0894834e.js index 925d2b33..0b6ea666 100644 --- a/assets/leverage-ai-to-boost-coding-productivity.html-fac3783c.js +++ b/assets/leverage-ai-to-boost-coding-productivity.html-0894834e.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-81a71778","path":"/daily/leverage-ai-to-boost-coding-productivity.html","title":"都什么年代了,还在用传统方式写代码?","lang":"zh-CN","frontmatter":{"date":"2023-08-26T00:00:00.000Z","tag":["AI","Daily"],"description":"都什么年代了,还在用传统方式写代码? 前言 还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。 本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。 本文将分享 AI 时代的编程新实践,教你如何从一个 \\"Ctrl + C\\"、 \\"Ctrl + V\\" 工程师,变成一个 \\"Tab + Enter\\" 工程师🤣。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/leverage-ai-to-boost-coding-productivity.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"都什么年代了,还在用传统方式写代码?"}],["meta",{"property":"og:description","content":"都什么年代了,还在用传统方式写代码? 前言 还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。 本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。 本文将分享 AI 时代的编程新实践,教你如何从一个 \\"Ctrl + C\\"、 \\"Ctrl + V\\" 工程师,变成一个 \\"Tab + Enter\\" 工程师🤣。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"AI"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-08-26T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"都什么年代了,还在用传统方式写代码?\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-26T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"开发流程","slug":"开发流程","link":"#开发流程","children":[]},{"level":2,"title":"程序设计","slug":"程序设计","link":"#程序设计","children":[]},{"level":2,"title":"代码编写","slug":"代码编写","link":"#代码编写","children":[{"level":3,"title":"生成真实代码","slug":"生成真实代码","link":"#生成真实代码","children":[]},{"level":3,"title":"辅助编程工具","slug":"辅助编程工具","link":"#辅助编程工具","children":[]}]},{"level":2,"title":"软件测试","slug":"软件测试","link":"#软件测试","children":[]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]},{"level":2,"title":"附:CodeWhisperer 安装","slug":"附-codewhisperer-安装","link":"#附-codewhisperer-安装","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":7.37,"words":2212},"filePathRelative":"daily/leverage-ai-to-boost-coding-productivity.md","localizedDate":"2023年8月26日","excerpt":"

都什么年代了,还在用传统方式写代码?

\\n

前言

\\n

还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

\\n

本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

\\n

本文将分享 AI 时代的编程新实践,教你如何从一个 \\"Ctrl + C\\"、 \\"Ctrl + V\\" 工程师,变成一个 \\"Tab + Enter\\" 工程师🤣。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-81a71778","path":"/daily/leverage-ai-to-boost-coding-productivity.html","title":"都什么年代了,还在用传统方式写代码?","lang":"zh-CN","frontmatter":{"date":"2023-08-26T00:00:00.000Z","tag":["AI","Daily"],"description":"都什么年代了,还在用传统方式写代码? 前言 还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。 本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。 本文将分享 AI 时代的编程新实践,教你如何从一个 \\"Ctrl + C\\"、 \\"Ctrl + V\\" 工程师,变成一个 \\"Tab + Enter\\" 工程师🤣。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/leverage-ai-to-boost-coding-productivity.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"都什么年代了,还在用传统方式写代码?"}],["meta",{"property":"og:description","content":"都什么年代了,还在用传统方式写代码? 前言 还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。 本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。 本文将分享 AI 时代的编程新实践,教你如何从一个 \\"Ctrl + C\\"、 \\"Ctrl + V\\" 工程师,变成一个 \\"Tab + Enter\\" 工程师🤣。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"AI"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-08-26T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"都什么年代了,还在用传统方式写代码?\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-26T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"开发流程","slug":"开发流程","link":"#开发流程","children":[]},{"level":2,"title":"程序设计","slug":"程序设计","link":"#程序设计","children":[]},{"level":2,"title":"代码编写","slug":"代码编写","link":"#代码编写","children":[{"level":3,"title":"生成真实代码","slug":"生成真实代码","link":"#生成真实代码","children":[]},{"level":3,"title":"辅助编程工具","slug":"辅助编程工具","link":"#辅助编程工具","children":[]}]},{"level":2,"title":"软件测试","slug":"软件测试","link":"#软件测试","children":[]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]},{"level":2,"title":"附:CodeWhisperer 安装","slug":"附-codewhisperer-安装","link":"#附-codewhisperer-安装","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":7.37,"words":2212},"filePathRelative":"daily/leverage-ai-to-boost-coding-productivity.md","localizedDate":"2023年8月26日","excerpt":"

都什么年代了,还在用传统方式写代码?

\\n

前言

\\n

还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

\\n

本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

\\n

本文将分享 AI 时代的编程新实践,教你如何从一个 \\"Ctrl + C\\"、 \\"Ctrl + V\\" 工程师,变成一个 \\"Tab + Enter\\" 工程师🤣。

","autoDesc":true}');export{e as data}; diff --git a/assets/leverage-ai-to-boost-coding-productivity.html-b3dab214.js b/assets/leverage-ai-to-boost-coding-productivity.html-77d0fee4.js similarity index 99% rename from assets/leverage-ai-to-boost-coding-productivity.html-b3dab214.js rename to assets/leverage-ai-to-boost-coding-productivity.html-77d0fee4.js index d3621558..dfa6fa2b 100644 --- a/assets/leverage-ai-to-boost-coding-productivity.html-b3dab214.js +++ b/assets/leverage-ai-to-boost-coding-productivity.html-77d0fee4.js @@ -1,4 +1,4 @@ -import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{o as s,c as a,f as t}from"./app-b649ee34.js";const p={},e=t(`

都什么年代了,还在用传统方式写代码?

前言

还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

本文将分享 AI 时代的编程新实践,教你如何从一个 "Ctrl + C"、 "Ctrl + V" 工程师,变成一个 "Tab + Enter" 工程师🤣。

开发流程

软件的一般研发流程为:

  1. 需求分析
  2. 程序设计
  3. 代码编写
  4. 软件测试
  5. 部署上线

我们在这里主要关心步骤2~4,因为与 AI 结合得比较紧密。虽然需求分析也可以借助 AI,但不是本文的重点,故不做讨论。

程序设计

经过需求分析、逻辑梳理后,在编写实际代码前,需要进行程序设计。

此环节的产物是设计文档,是什么类型的设计文档不重要,重要的是伪代码的输出。

虽然《Code Complete》早就推荐过伪代码的实践,但对此人们容易有一个误区:认为写伪代码花的时间,已经够把实际代码写好了。但 AIGC 时代,此问题可以轻松破解:AI 写代码的速度肯定比人快,因此,只要能找到方法能让 AI 生成符合需求的代码,就值得花时间去研究。而伪代码,就是让 AI 快速生成符合期望的实际代码的最好方式。

为什么这么说呢?因为想要让 AIGC 符合期望,恰当的 Prompt 必不可少。但如何写好这个 Prompt,需要提供多少上下文,才能让 AI 更好地理解我们的意图,这是需要技巧、需要调试的。而经过精心设计的伪代码,本身已经提供了足够的上下文,且意图足够聚焦,减轻了对 Prompt 的要求,又提高了 AIGC 的成功率。

我们来看一下伪代码示例:

plainText = JSON.stringify(data)
+import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{o as s,c as a,f as t}from"./app-a9d55428.js";const p={},e=t(`

都什么年代了,还在用传统方式写代码?

前言

还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

本文将分享 AI 时代的编程新实践,教你如何从一个 "Ctrl + C"、 "Ctrl + V" 工程师,变成一个 "Tab + Enter" 工程师🤣。

开发流程

软件的一般研发流程为:

  1. 需求分析
  2. 程序设计
  3. 代码编写
  4. 软件测试
  5. 部署上线

我们在这里主要关心步骤2~4,因为与 AI 结合得比较紧密。虽然需求分析也可以借助 AI,但不是本文的重点,故不做讨论。

程序设计

经过需求分析、逻辑梳理后,在编写实际代码前,需要进行程序设计。

此环节的产物是设计文档,是什么类型的设计文档不重要,重要的是伪代码的输出。

虽然《Code Complete》早就推荐过伪代码的实践,但对此人们容易有一个误区:认为写伪代码花的时间,已经够把实际代码写好了。但 AIGC 时代,此问题可以轻松破解:AI 写代码的速度肯定比人快,因此,只要能找到方法能让 AI 生成符合需求的代码,就值得花时间去研究。而伪代码,就是让 AI 快速生成符合期望的实际代码的最好方式。

为什么这么说呢?因为想要让 AIGC 符合期望,恰当的 Prompt 必不可少。但如何写好这个 Prompt,需要提供多少上下文,才能让 AI 更好地理解我们的意图,这是需要技巧、需要调试的。而经过精心设计的伪代码,本身已经提供了足够的上下文,且意图足够聚焦,减轻了对 Prompt 的要求,又提高了 AIGC 的成功率。

我们来看一下伪代码示例:

plainText = JSON.stringify(data)
 digest = hash(plainText) // 防篡改
 secret = Symmetric.getKey()  
 cipherText = encryptText(data, secret) // 防内容泄密
diff --git a/assets/mysql-backup-case-study-mysqldump-in-action.html-cea2b606.js b/assets/mysql-backup-case-study-mysqldump-in-action.html-73cda0a5.js
similarity index 90%
rename from assets/mysql-backup-case-study-mysqldump-in-action.html-cea2b606.js
rename to assets/mysql-backup-case-study-mysqldump-in-action.html-73cda0a5.js
index e7c29007..29d5585d 100644
--- a/assets/mysql-backup-case-study-mysqldump-in-action.html-cea2b606.js
+++ b/assets/mysql-backup-case-study-mysqldump-in-action.html-73cda0a5.js
@@ -1 +1 @@
-const e=JSON.parse(`{"key":"v-143a8bce","path":"/mysql/mysql-backup-case-study-mysqldump-in-action.html","title":"数据备份案例:mysqldump实战","lang":"zh-CN","frontmatter":{"date":"2023-08-17T00:00:00.000Z","tag":["Daily","MySQL"],"description":"数据备份案例:mysqldump实战 背景 前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。 并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。 在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。","head":[["meta",{"property":"og:url","content":"https://levy.vip/mysql/mysql-backup-case-study-mysqldump-in-action.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"数据备份案例:mysqldump实战"}],["meta",{"property":"og:description","content":"数据备份案例:mysqldump实战 背景 前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。 并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。 在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:published_time","content":"2023-08-17T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"数据备份案例:mysqldump实战\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-17T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"架构","slug":"架构","link":"#架构","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"导出","slug":"导出","link":"#导出","children":[{"level":3,"title":"--set-gtid-purged=OFF","slug":"set-gtid-purged-off","link":"#set-gtid-purged-off","children":[]},{"level":3,"title":"--ignore-table","slug":"ignore-table","link":"#ignore-table","children":[]}]},{"level":2,"title":"导入","slug":"导入","link":"#导入","children":[]},{"level":2,"title":"为什么不?","slug":"为什么不","link":"#为什么不","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.1,"words":1231},"filePathRelative":"mysql/mysql-backup-case-study-mysqldump-in-action.md","localizedDate":"2023年8月17日","excerpt":"

数据备份案例:mysqldump实战

\\n

背景

\\n

前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。

\\n

并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。

\\n

在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。

\\n","autoDesc":true}`);export{e as data}; +const e=JSON.parse(`{"key":"v-143a8bce","path":"/mysql/mysql-backup-case-study-mysqldump-in-action.html","title":"数据备份案例:mysqldump实战","lang":"zh-CN","frontmatter":{"date":"2023-08-17T00:00:00.000Z","tag":["Daily","MySQL"],"description":"数据备份案例:mysqldump实战 背景 前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。 并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。 在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。","head":[["meta",{"property":"og:url","content":"https://levy.vip/mysql/mysql-backup-case-study-mysqldump-in-action.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"数据备份案例:mysqldump实战"}],["meta",{"property":"og:description","content":"数据备份案例:mysqldump实战 背景 前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。 并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。 在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:published_time","content":"2023-08-17T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"数据备份案例:mysqldump实战\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-17T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"架构","slug":"架构","link":"#架构","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"导出","slug":"导出","link":"#导出","children":[{"level":3,"title":"--set-gtid-purged=OFF","slug":"set-gtid-purged-off","link":"#set-gtid-purged-off","children":[]},{"level":3,"title":"--ignore-table","slug":"ignore-table","link":"#ignore-table","children":[]}]},{"level":2,"title":"导入","slug":"导入","link":"#导入","children":[]},{"level":2,"title":"为什么不?","slug":"为什么不","link":"#为什么不","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.1,"words":1231},"filePathRelative":"mysql/mysql-backup-case-study-mysqldump-in-action.md","localizedDate":"2023年8月17日","excerpt":"

数据备份案例:mysqldump实战

\\n

背景

\\n

前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。

\\n

并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。

\\n

在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。

\\n","autoDesc":true}`);export{e as data}; diff --git a/assets/mysql-backup-case-study-mysqldump-in-action.html-27652577.js b/assets/mysql-backup-case-study-mysqldump-in-action.html-b5eeab0a.js similarity index 99% rename from assets/mysql-backup-case-study-mysqldump-in-action.html-27652577.js rename to assets/mysql-backup-case-study-mysqldump-in-action.html-b5eeab0a.js index 617d813c..2efb043b 100644 --- a/assets/mysql-backup-case-study-mysqldump-in-action.html-27652577.js +++ b/assets/mysql-backup-case-study-mysqldump-in-action.html-b5eeab0a.js @@ -1,4 +1,4 @@ -import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o as l,c as t,a,b as s,d as r,w as o,e as p,f as d}from"./app-b649ee34.js";const c={},u=a("h1",{id:"数据备份案例-mysqldump实战",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#数据备份案例-mysqldump实战","aria-hidden":"true"},"#"),s(" 数据备份案例:mysqldump实战")],-1),m=a("h2",{id:"背景",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#背景","aria-hidden":"true"},"#"),s(" 背景")],-1),b=a("p",null,"并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。",-1),h=a("p",null,"在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。",-1),v=d(`

架构


注意到:

  1. 从 mysql-b -> mysql-b',就要用到工具 mysqldump
  2. 关于 sql 的编写在另一文中已有提及,就不重复了
  3. 只能通过跳板机在终端连接数据库实例,因此无法使用图例界面操作。

安装

首先要在跳板机安装 mysqldump。

如果够幸运,可以用包管理工具安装:

sudo apt update
+import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o as l,c as t,a,b as s,d as r,w as o,e as p,f as d}from"./app-a9d55428.js";const c={},u=a("h1",{id:"数据备份案例-mysqldump实战",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#数据备份案例-mysqldump实战","aria-hidden":"true"},"#"),s(" 数据备份案例:mysqldump实战")],-1),m=a("h2",{id:"背景",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#背景","aria-hidden":"true"},"#"),s(" 背景")],-1),b=a("p",null,"并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。",-1),h=a("p",null,"在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。",-1),v=d(`

架构


注意到:

  1. 从 mysql-b -> mysql-b',就要用到工具 mysqldump
  2. 关于 sql 的编写在另一文中已有提及,就不重复了
  3. 只能通过跳板机在终端连接数据库实例,因此无法使用图例界面操作。

安装

首先要在跳板机安装 mysqldump。

如果够幸运,可以用包管理工具安装:

sudo apt update
 
 sudo apt install mysql-client
 
sudo yum install https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
diff --git a/assets/mysql-data-migration-case-study-add-auto-increment.html-6018956d.js b/assets/mysql-data-migration-case-study-add-auto-increment.html-6018956d.js
new file mode 100644
index 00000000..c5680015
--- /dev/null
+++ b/assets/mysql-data-migration-case-study-add-auto-increment.html-6018956d.js
@@ -0,0 +1 @@
+const e=JSON.parse('{"key":"v-7e2c7a0c","path":"/mysql/mysql-data-migration-case-study-add-auto-increment.html","title":"数据迁移案例:表AUTO_INCREMENT加10w","lang":"zh-CN","frontmatter":{"date":"2023-08-16T00:00:00.000Z","tag":["Daily","MySQL"],"description":"数据迁移案例:表AUTO_INCREMENT加10w 背景 项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。 问题分析: 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。 迁移思路: 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条) mysql-b 的相应的表 AUTO_INCREMENT 加 10w 记得动手前先确保数据已备份 注意: mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。","head":[["meta",{"property":"og:url","content":"https://levy.vip/mysql/mysql-data-migration-case-study-add-auto-increment.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"数据迁移案例:表AUTO_INCREMENT加10w"}],["meta",{"property":"og:description","content":"数据迁移案例:表AUTO_INCREMENT加10w 背景 项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。 问题分析: 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。 迁移思路: 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条) mysql-b 的相应的表 AUTO_INCREMENT 加 10w 记得动手前先确保数据已备份 注意: mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:published_time","content":"2023-08-16T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"数据迁移案例:表AUTO_INCREMENT加10w\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-16T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"备份","slug":"备份","link":"#备份","children":[]},{"level":2,"title":"SQL编写","slug":"sql编写","link":"#sql编写","children":[]},{"level":2,"title":"存储过程(可复用","slug":"存储过程-可复用","link":"#存储过程-可复用","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2,"words":599},"filePathRelative":"mysql/mysql-data-migration-case-study-add-auto-increment.md","localizedDate":"2023年8月16日","excerpt":"

数据迁移案例:表AUTO_INCREMENT加10w

\\n

背景

\\n

项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。

\\n

问题分析:

\\n
    \\n
  1. 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。
  2. \\n
  3. 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。
  4. \\n
\\n

迁移思路:

\\n
    \\n
  1. 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条)
  2. \\n
  3. mysql-b 的相应的表 AUTO_INCREMENT 加 10w
  4. \\n
  5. 记得动手前先确保数据已备份
  6. \\n
\\n

注意:

\\n
    \\n
  • mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。
  • \\n
  • 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。
  • \\n
\\n","autoDesc":true}');export{e as data}; diff --git a/assets/mysql-data-migration-case-study-add-auto-increment.html-75552c95.js b/assets/mysql-data-migration-case-study-add-auto-increment.html-75552c95.js deleted file mode 100644 index 3f7b9503..00000000 --- a/assets/mysql-data-migration-case-study-add-auto-increment.html-75552c95.js +++ /dev/null @@ -1 +0,0 @@ -const e=JSON.parse('{"key":"v-7e2c7a0c","path":"/mysql/mysql-data-migration-case-study-add-auto-increment.html","title":"数据迁移案例:表AUTO_INCREMENT加10w","lang":"zh-CN","frontmatter":{"date":"2023-08-16T00:00:00.000Z","tag":["Daily","MySQL"],"description":"数据迁移案例:表AUTO_INCREMENT加10w 背景 项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。 问题分析: 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。 迁移思路: 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条) mysql-b 的相应的表 AUTO_INCREMENT 加 10w 记得动手前先确保数据已备份 注意:mysql-b的数据量比mysql-a的大,所以 mysql-b也直接设置AUTO_INCREMENT 加10w。 但如果mysql-a的数量比较大,那是不可行的,此时mysql-b 需要先select 出每张表的最大id,作为需要增加的AUTO_INCREMENT。","head":[["meta",{"property":"og:url","content":"https://levy.vip/mysql/mysql-data-migration-case-study-add-auto-increment.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"数据迁移案例:表AUTO_INCREMENT加10w"}],["meta",{"property":"og:description","content":"数据迁移案例:表AUTO_INCREMENT加10w 背景 项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。 问题分析: 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。 迁移思路: 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条) mysql-b 的相应的表 AUTO_INCREMENT 加 10w 记得动手前先确保数据已备份 注意:mysql-b的数据量比mysql-a的大,所以 mysql-b也直接设置AUTO_INCREMENT 加10w。 但如果mysql-a的数量比较大,那是不可行的,此时mysql-b 需要先select 出每张表的最大id,作为需要增加的AUTO_INCREMENT。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:published_time","content":"2023-08-16T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"数据迁移案例:表AUTO_INCREMENT加10w\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-16T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"备份","slug":"备份","link":"#备份","children":[]},{"level":2,"title":"SQL编写","slug":"sql编写","link":"#sql编写","children":[]},{"level":2,"title":"存储过程(可复用","slug":"存储过程-可复用","link":"#存储过程-可复用","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2,"words":599},"filePathRelative":"mysql/mysql-data-migration-case-study-add-auto-increment.md","localizedDate":"2023年8月16日","excerpt":"

数据迁移案例:表AUTO_INCREMENT加10w

\\n

背景

\\n

项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。

\\n

问题分析:

\\n
    \\n
  1. 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。
  2. \\n
  3. 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。
  4. \\n
\\n

迁移思路:

\\n
    \\n
  1. 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条)
  2. \\n
  3. mysql-b 的相应的表 AUTO_INCREMENT 加 10w
  4. \\n
  5. 记得动手前先确保数据已备份
  6. \\n
\\n

注意:mysql-b的数据量比mysql-a的大,所以 mysql-b也直接设置AUTO_INCREMENT 加10w。
\\n但如果mysql-a的数量比较大,那是不可行的,此时mysql-b 需要先select 出每张表的最大id,作为需要增加的AUTO_INCREMENT。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/mysql-data-migration-case-study-add-auto-increment.html-0e5bf45b.js b/assets/mysql-data-migration-case-study-add-auto-increment.html-b3a99eec.js similarity index 90% rename from assets/mysql-data-migration-case-study-add-auto-increment.html-0e5bf45b.js rename to assets/mysql-data-migration-case-study-add-auto-increment.html-b3a99eec.js index 5cde1312..f736a40a 100644 --- a/assets/mysql-data-migration-case-study-add-auto-increment.html-0e5bf45b.js +++ b/assets/mysql-data-migration-case-study-add-auto-increment.html-b3a99eec.js @@ -1,4 +1,4 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o,c as l,e as c,a as n,b as s,d as i,w as r,f as a}from"./app-b649ee34.js";const d={},u=a('

数据迁移案例:表AUTO_INCREMENT加10w

背景

项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。

问题分析:

  1. 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。
  2. 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。

迁移思路:

  1. 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条)
  2. mysql-b 的相应的表 AUTO_INCREMENT 加 10w
  3. 记得动手前先确保数据已备份

注意:mysql-b的数据量比mysql-a的大,所以 mysql-b也直接设置AUTO_INCREMENT 加10w。
但如果mysql-a的数量比较大,那是不可行的,此时mysql-b 需要先select 出每张表的最大id,作为需要增加的AUTO_INCREMENT。

',8),k=n("h2",{id:"备份",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#备份","aria-hidden":"true"},"#"),s(" 备份")],-1),m=a(`

SQL编写

mysql-a 可以先 insert:

insert into table_a(id, other_fields...)
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o,c as l,e as c,a as n,b as s,d as i,w as r,f as a}from"./app-a9d55428.js";const d={},u=a('

数据迁移案例:表AUTO_INCREMENT加10w

背景

项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。

问题分析:

  1. 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。
  2. 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。

迁移思路:

  1. 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条)
  2. mysql-b 的相应的表 AUTO_INCREMENT 加 10w
  3. 记得动手前先确保数据已备份

注意:

  • mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。
  • 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。
',9),k=n("h2",{id:"备份",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#备份","aria-hidden":"true"},"#"),s(" 备份")],-1),m=a(`

SQL编写

mysql-a 可以先 insert:

insert into table_a(id, other_fields...)
 select id+100000, other_fields... from table_a
 where is_deleted=0;
 

再导出新增的数据:

select * 
@@ -26,7 +26,7 @@ import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o,c as
   EXECUTE stmt;
   DEALLOCATE PREPARE stmt;
 END//
-DELIMITER ;�
+DELIMITER ;
 
CALL AddAutoIncrementValue('table_a', 100000);
 CALL AddAutoIncrementValue('tablb_b', 100000);
 

上述代码可复用,直接复制粘贴即可,有需要的请自取。

`,20);function v(b,y){const e=p("RouterLink");return o(),l("div",null,[u,c(" more "),k,n("p",null,[s("迁移前一定要做好备份。备份的技巧在"),i(e,{to:"/mysql/mysql-backup-case-study-mysqldump-in-action.html"},{default:r(()=>[s("另一篇文章")]),_:1}),s("里有讲,就不在此赘述了。")]),m])}const h=t(d,[["render",v],["__file","mysql-data-migration-case-study-add-auto-increment.html.vue"]]);export{h as default}; diff --git a/assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-9a9e576f.js b/assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-3a6f2dc3.js similarity index 91% rename from assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-9a9e576f.js rename to assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-3a6f2dc3.js index 90e8ce19..09df29b9 100644 --- a/assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-9a9e576f.js +++ b/assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-3a6f2dc3.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-f297935a","path":"/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html","title":"MySQL 命令行执行SQL的细节","lang":"zh-CN","frontmatter":{"date":"2023-08-19T00:00:00.000Z","tag":["Daily","MySQL"],"description":"MySQL 命令行执行SQL的细节 背景 经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了! 但要小心,在正式环境中执行 SQL,也许会有意想不到的坑! 环境说明 先说明下我们的环境信息。 我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。","head":[["meta",{"property":"og:url","content":"https://levy.vip/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"MySQL 命令行执行SQL的细节"}],["meta",{"property":"og:description","content":"MySQL 命令行执行SQL的细节 背景 经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了! 但要小心,在正式环境中执行 SQL,也许会有意想不到的坑! 环境说明 先说明下我们的环境信息。 我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:published_time","content":"2023-08-19T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"MySQL 命令行执行SQL的细节\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-19T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"环境说明","slug":"环境说明","link":"#环境说明","children":[]},{"level":2,"title":"执行SQL文件","slug":"执行sql文件","link":"#执行sql文件","children":[]},{"level":2,"title":"复制粘贴执行","slug":"复制粘贴执行","link":"#复制粘贴执行","children":[]},{"level":2,"title":"如果要删除错误的数据怎么办?","slug":"如果要删除错误的数据怎么办","link":"#如果要删除错误的数据怎么办","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.02,"words":906},"filePathRelative":"mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.md","localizedDate":"2023年8月19日","excerpt":"

MySQL 命令行执行SQL的细节

\\n

背景

\\n

经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了!

\\n

但要小心,在正式环境中执行 SQL,也许会有意想不到的坑!

\\n

环境说明

\\n

先说明下我们的环境信息。
\\n\\"\\"
\\n我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-f297935a","path":"/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html","title":"MySQL 命令行执行SQL的细节","lang":"zh-CN","frontmatter":{"date":"2023-08-19T00:00:00.000Z","tag":["Daily","MySQL"],"description":"MySQL 命令行执行SQL的细节 背景 经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了! 但要小心,在正式环境中执行 SQL,也许会有意想不到的坑! 环境说明 先说明下我们的环境信息。 我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。","head":[["meta",{"property":"og:url","content":"https://levy.vip/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"MySQL 命令行执行SQL的细节"}],["meta",{"property":"og:description","content":"MySQL 命令行执行SQL的细节 背景 经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了! 但要小心,在正式环境中执行 SQL,也许会有意想不到的坑! 环境说明 先说明下我们的环境信息。 我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:published_time","content":"2023-08-19T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"MySQL 命令行执行SQL的细节\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-19T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"环境说明","slug":"环境说明","link":"#环境说明","children":[]},{"level":2,"title":"执行SQL文件","slug":"执行sql文件","link":"#执行sql文件","children":[]},{"level":2,"title":"复制粘贴执行","slug":"复制粘贴执行","link":"#复制粘贴执行","children":[]},{"level":2,"title":"如果要删除错误的数据怎么办?","slug":"如果要删除错误的数据怎么办","link":"#如果要删除错误的数据怎么办","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.02,"words":906},"filePathRelative":"mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.md","localizedDate":"2023年8月19日","excerpt":"

MySQL 命令行执行SQL的细节

\\n

背景

\\n

经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了!

\\n

但要小心,在正式环境中执行 SQL,也许会有意想不到的坑!

\\n

环境说明

\\n

先说明下我们的环境信息。
\\n\\"\\"
\\n我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。

","autoDesc":true}');export{e as data}; diff --git a/assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-834b3dd8.js b/assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-e11c76db.js similarity index 98% rename from assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-834b3dd8.js rename to assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-e11c76db.js index 5c371088..2dd5f35a 100644 --- a/assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-834b3dd8.js +++ b/assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-e11c76db.js @@ -1,4 +1,4 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as r,c as o,a,b as e,d as i,f as s}from"./app-b649ee34.js";const p={},d=s(`

MySQL 命令行执行SQL的细节

背景

经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了!

但要小心,在正式环境中执行 SQL,也许会有意想不到的坑!

环境说明

先说明下我们的环境信息。

我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。

则我们执行SQL语句的方式有两种:

  1. 执行导出的SQL语句文件
  2. 复制粘贴SQL语句执行

当然,在执行前,我们要确保已进行了数据备份。

执行SQL文件

执行SQL文件是最简单的,一般实践也是在命令行批量执行SQL文件。

相关的命令与恢复备份的命令一致:

mysql -h your-ip -u your-username -p\${password} your-database <  script.sql
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as r,c as o,a,b as e,d as i,f as s}from"./app-a9d55428.js";const p={},d=s(`

MySQL 命令行执行SQL的细节

背景

经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了!

但要小心,在正式环境中执行 SQL,也许会有意想不到的坑!

环境说明

先说明下我们的环境信息。

我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。

则我们执行SQL语句的方式有两种:

  1. 执行导出的SQL语句文件
  2. 复制粘贴SQL语句执行

当然,在执行前,我们要确保已进行了数据备份。

执行SQL文件

执行SQL文件是最简单的,一般实践也是在命令行批量执行SQL文件。

相关的命令与恢复备份的命令一致:

mysql -h your-ip -u your-username -p\${password} your-database <  script.sql
 

这是推荐的方式,因为执行语句一旦出错,就会停下,并告知是第几行的语句出错。

但出于某些原因,你可能不想把所有SQL语句都合并到一个 script.sql 文件中。
另外,上传文件到跳板机,可能也比较麻烦,于是,你想采用第二种方式。

复制粘贴执行

通过 mysql 客户端直接上 MySQL 后,在命令上执行 SQL 语句会有一个问题:错误的语句不会中断后续的执行。

更可怕的是在命令行里,很可能你SQL语句包括的中文字符串会被过滤掉,变成空字符串。如:

  • '创建人' -> ''
  • '中英en混杂' -> 'en'

这真是血的教训😭。

我们先来看下错误是否中断的实验。

新建一个只有两行的SQL文件,其中第一行语句是错误的。

ss;
 select 1;
 

使用导入命令:

mysql -h your-ip -u your-username -p\${password} your-database < test.sql
diff --git a/assets/old-articles.html-3e4d9bf0.js b/assets/old-articles.html-6079e753.js
similarity index 92%
rename from assets/old-articles.html-3e4d9bf0.js
rename to assets/old-articles.html-6079e753.js
index e278a4d7..8811b166 100644
--- a/assets/old-articles.html-3e4d9bf0.js
+++ b/assets/old-articles.html-6079e753.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-72e84a92","path":"/frontend/old-articles.html","title":"旧文章精选","lang":"zh-CN","frontmatter":{"date":"2019-09-03T00:00:00.000Z","tag":"Frontend","description":"旧文章精选 📦vue组件发布npm最佳实践 🔨揭秘vue-sfc-cli:组件研发利器 🚀Github集成TravisCI:自动发布 ⚡Github集成Netlify:快速预览PR 🌪自动化的Github Workflow 🤖如何写一个GithubApp 🔒免费开启HTTPS 🕸捕获与改写HTTPS请求","head":[["meta",{"property":"og:url","content":"https://levy.vip/frontend/old-articles.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"旧文章精选"}],["meta",{"property":"og:description","content":"旧文章精选 📦vue组件发布npm最佳实践 🔨揭秘vue-sfc-cli:组件研发利器 🚀Github集成TravisCI:自动发布 ⚡Github集成Netlify:快速预览PR 🌪自动化的Github Workflow 🤖如何写一个GithubApp 🔒免费开启HTTPS 🕸捕获与改写HTTPS请求"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:published_time","content":"2019-09-03T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"旧文章精选\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2019-09-03T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.3,"words":90},"filePathRelative":"frontend/old-articles.md","localizedDate":"2019年9月3日","excerpt":"

旧文章精选

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-72e84a92","path":"/frontend/old-articles.html","title":"旧文章精选","lang":"zh-CN","frontmatter":{"date":"2019-09-03T00:00:00.000Z","tag":"Frontend","description":"旧文章精选 📦vue组件发布npm最佳实践 🔨揭秘vue-sfc-cli:组件研发利器 🚀Github集成TravisCI:自动发布 ⚡Github集成Netlify:快速预览PR 🌪自动化的Github Workflow 🤖如何写一个GithubApp 🔒免费开启HTTPS 🕸捕获与改写HTTPS请求","head":[["meta",{"property":"og:url","content":"https://levy.vip/frontend/old-articles.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"旧文章精选"}],["meta",{"property":"og:description","content":"旧文章精选 📦vue组件发布npm最佳实践 🔨揭秘vue-sfc-cli:组件研发利器 🚀Github集成TravisCI:自动发布 ⚡Github集成Netlify:快速预览PR 🌪自动化的Github Workflow 🤖如何写一个GithubApp 🔒免费开启HTTPS 🕸捕获与改写HTTPS请求"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:published_time","content":"2019-09-03T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"旧文章精选\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2019-09-03T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.3,"words":90},"filePathRelative":"frontend/old-articles.md","localizedDate":"2019年9月3日","excerpt":"

旧文章精选

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/old-articles.html-3441ffdc.js b/assets/old-articles.html-ec567119.js similarity index 96% rename from assets/old-articles.html-3441ffdc.js rename to assets/old-articles.html-ec567119.js index f5b9d1e0..7e3fdbf2 100644 --- a/assets/old-articles.html-3441ffdc.js +++ b/assets/old-articles.html-ec567119.js @@ -1 +1 @@ -import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as s,c as i,a as e,b as r,d as o}from"./app-b649ee34.js";const a={},h=e("h1",{id:"旧文章精选",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#旧文章精选","aria-hidden":"true"},"#"),r(" 旧文章精选")],-1),c={href:"https://github.com/levy9527/blog/issues/2",target:"_blank",rel:"noopener noreferrer"},_={href:"https://github.com/levy9527/blog/issues/7",target:"_blank",rel:"noopener noreferrer"},u={href:"https://github.com/levy9527/blog/issues/1",target:"_blank",rel:"noopener noreferrer"},b={href:"https://github.com/levy9527/blog/issues/4",target:"_blank",rel:"noopener noreferrer"},f={href:"https://github.com/levy9527/blog/issues/12",target:"_blank",rel:"noopener noreferrer"},p={href:"https://github.com/levy9527/blog/issues/10",target:"_blank",rel:"noopener noreferrer"},d={href:"https://github.com/levy9527/blog/issues/5",target:"_blank",rel:"noopener noreferrer"},g={href:"https://github.com/levy9527/blog/issues/11",target:"_blank",rel:"noopener noreferrer"};function m(v,k){const t=n("ExternalLinkIcon");return s(),i("div",null,[h,e("ul",null,[e("li",null,[e("a",c,[r("📦vue组件发布npm最佳实践"),o(t)])]),e("li",null,[e("a",_,[r("🔨揭秘vue-sfc-cli:组件研发利器"),o(t)])]),e("li",null,[e("a",u,[r("🚀Github集成TravisCI:自动发布"),o(t)])]),e("li",null,[e("a",b,[r("⚡Github集成Netlify:快速预览PR"),o(t)])]),e("li",null,[e("a",f,[r("🌪自动化的Github Workflow"),o(t)])]),e("li",null,[e("a",p,[r("🤖如何写一个GithubApp"),o(t)])]),e("li",null,[e("a",d,[r("🔒免费开启HTTPS"),o(t)])]),e("li",null,[e("a",g,[r("🕸捕获与改写HTTPS请求"),o(t)])])])])}const T=l(a,[["render",m],["__file","old-articles.html.vue"]]);export{T as default}; +import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as s,c as i,a as e,b as r,d as o}from"./app-a9d55428.js";const a={},h=e("h1",{id:"旧文章精选",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#旧文章精选","aria-hidden":"true"},"#"),r(" 旧文章精选")],-1),c={href:"https://github.com/levy9527/blog/issues/2",target:"_blank",rel:"noopener noreferrer"},_={href:"https://github.com/levy9527/blog/issues/7",target:"_blank",rel:"noopener noreferrer"},u={href:"https://github.com/levy9527/blog/issues/1",target:"_blank",rel:"noopener noreferrer"},b={href:"https://github.com/levy9527/blog/issues/4",target:"_blank",rel:"noopener noreferrer"},f={href:"https://github.com/levy9527/blog/issues/12",target:"_blank",rel:"noopener noreferrer"},p={href:"https://github.com/levy9527/blog/issues/10",target:"_blank",rel:"noopener noreferrer"},d={href:"https://github.com/levy9527/blog/issues/5",target:"_blank",rel:"noopener noreferrer"},g={href:"https://github.com/levy9527/blog/issues/11",target:"_blank",rel:"noopener noreferrer"};function m(v,k){const t=n("ExternalLinkIcon");return s(),i("div",null,[h,e("ul",null,[e("li",null,[e("a",c,[r("📦vue组件发布npm最佳实践"),o(t)])]),e("li",null,[e("a",_,[r("🔨揭秘vue-sfc-cli:组件研发利器"),o(t)])]),e("li",null,[e("a",u,[r("🚀Github集成TravisCI:自动发布"),o(t)])]),e("li",null,[e("a",b,[r("⚡Github集成Netlify:快速预览PR"),o(t)])]),e("li",null,[e("a",f,[r("🌪自动化的Github Workflow"),o(t)])]),e("li",null,[e("a",p,[r("🤖如何写一个GithubApp"),o(t)])]),e("li",null,[e("a",d,[r("🔒免费开启HTTPS"),o(t)])]),e("li",null,[e("a",g,[r("🕸捕获与改写HTTPS请求"),o(t)])])])])}const T=l(a,[["render",m],["__file","old-articles.html.vue"]]);export{T as default}; diff --git a/assets/performance-optimization-in-action.html-f6072ca8.js b/assets/performance-optimization-in-action.html-0aff1283.js similarity index 91% rename from assets/performance-optimization-in-action.html-f6072ca8.js rename to assets/performance-optimization-in-action.html-0aff1283.js index 2a752997..24ce5610 100644 --- a/assets/performance-optimization-in-action.html-f6072ca8.js +++ b/assets/performance-optimization-in-action.html-0aff1283.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-7320140c","path":"/frontend/performance-optimization-in-action.html","title":"前端项目性能优化实战","lang":"zh-CN","frontmatter":{"date":"2021-01-27T00:00:00.000Z","tag":"Frontend","description":"前端项目性能优化实战 本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。","head":[["meta",{"property":"og:url","content":"https://levy.vip/frontend/performance-optimization-in-action.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"前端项目性能优化实战"}],["meta",{"property":"og:description","content":"前端项目性能优化实战 本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:published_time","content":"2021-01-27T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"前端项目性能优化实战\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2021-01-27T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"检测","slug":"检测","link":"#检测","children":[]},{"level":2,"title":"图片优化","slug":"图片优化","link":"#图片优化","children":[]},{"level":2,"title":"提高TTFB时间","slug":"提高ttfb时间","link":"#提高ttfb时间","children":[]},{"level":2,"title":"移除未使用的 Javascript","slug":"移除未使用的-javascript","link":"#移除未使用的-javascript","children":[]},{"level":2,"title":"延迟静态资源的加载","slug":"延迟静态资源的加载","link":"#延迟静态资源的加载","children":[]},{"level":2,"title":"启用文本压缩","slug":"启用文本压缩","link":"#启用文本压缩","children":[]},{"level":2,"title":"优化缓存策略","slug":"优化缓存策略","link":"#优化缓存策略","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.69,"words":1407},"filePathRelative":"frontend/performance-optimization-in-action.md","localizedDate":"2021年1月27日","excerpt":"

前端项目性能优化实战

\\n

本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-7320140c","path":"/frontend/performance-optimization-in-action.html","title":"前端项目性能优化实战","lang":"zh-CN","frontmatter":{"date":"2021-01-27T00:00:00.000Z","tag":"Frontend","description":"前端项目性能优化实战 本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。","head":[["meta",{"property":"og:url","content":"https://levy.vip/frontend/performance-optimization-in-action.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"前端项目性能优化实战"}],["meta",{"property":"og:description","content":"前端项目性能优化实战 本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:published_time","content":"2021-01-27T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"前端项目性能优化实战\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2021-01-27T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"检测","slug":"检测","link":"#检测","children":[]},{"level":2,"title":"图片优化","slug":"图片优化","link":"#图片优化","children":[]},{"level":2,"title":"提高TTFB时间","slug":"提高ttfb时间","link":"#提高ttfb时间","children":[]},{"level":2,"title":"移除未使用的 Javascript","slug":"移除未使用的-javascript","link":"#移除未使用的-javascript","children":[]},{"level":2,"title":"延迟静态资源的加载","slug":"延迟静态资源的加载","link":"#延迟静态资源的加载","children":[]},{"level":2,"title":"启用文本压缩","slug":"启用文本压缩","link":"#启用文本压缩","children":[]},{"level":2,"title":"优化缓存策略","slug":"优化缓存策略","link":"#优化缓存策略","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.69,"words":1407},"filePathRelative":"frontend/performance-optimization-in-action.md","localizedDate":"2021年1月27日","excerpt":"

前端项目性能优化实战

\\n

本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/performance-optimization-in-action.html-963023b4.js b/assets/performance-optimization-in-action.html-fa684067.js similarity index 99% rename from assets/performance-optimization-in-action.html-963023b4.js rename to assets/performance-optimization-in-action.html-fa684067.js index c589b958..31c0ca5e 100644 --- a/assets/performance-optimization-in-action.html-963023b4.js +++ b/assets/performance-optimization-in-action.html-fa684067.js @@ -1,4 +1,4 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o as l,c as p,e as r,a as n,b as a,d as e,f as t}from"./app-b649ee34.js";const c={},d=n("h1",{id:"前端项目性能优化实战",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#前端项目性能优化实战","aria-hidden":"true"},"#"),a(" 前端项目性能优化实战")],-1),u=n("p",null,"本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。",-1),m=n("h2",{id:"检测",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#检测","aria-hidden":"true"},"#"),a(" 检测")],-1),h=n("p",null,"使用两个工具分析项目首页性能情况:",-1),g={href:"https://developers.google.com/speed/pagespeed/insights/",target:"_blank",rel:"noopener noreferrer"},b={href:"https://tools.pingdom.com/",target:"_blank",rel:"noopener noreferrer"},k=n("p",null,[a("得到结果如下:"),n("br"),n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1611714344531-6501362c-526d-40e5-b1a4-a3399a544ea1.png",alt:"",loading:"lazy"}),n("br"),n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1607738629800-10ff9829-ea0c-402b-b826-1d05903ee302.png",alt:"",loading:"lazy"}),n("br"),a(" 可以看到,首页超过50%的请求都与图片有空,优化空间比较大,因此第一步应该是优化图片加载。")],-1),v=n("h2",{id:"图片优化",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#图片优化","aria-hidden":"true"},"#"),a(" 图片优化")],-1),f=n("br",null,null,-1),_=n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1607739432571-c4ebd1bb-bf91-4ced-afca-ad70b84c4de6.png",alt:"",loading:"lazy"},null,-1),y=n("br",null,null,-1),w={href:"https://zhuanlan.zhihu.com/p/99769484",target:"_blank",rel:"noopener noreferrer"},x=t('
  1. <img>  元素修改成 <v-img/> ,注意设置 width 或 height
  2. <div ``style="background-image: url(img-url)"``></div> 修改成 <div v-img="{src: img-url}"></div>

注意:img-url 应该是 oss 的链接,并且是 https 协议。
如果是 http 协议,或不指定协议 //img-url,则很可能会出现下图的情况:

如果图片是放在项目中,且项目并没有部署到 oss,则无法享受自动加载 webp 格式图片的福利。

则在此环节,一次性做到了上图中的三个优化点。

提高TTFB时间

来看下一条优化建议:

因为项目是服务端渲染的,有些请求是在服务端做了。找到相关代码:

经过分析,上述代码存在两个问题:

  1. 可在客户端执行却放在了服务端
  2. 可并行执行却写成了串行

修改如下:


',10),z=n("br",null,null,-1),C=n("br",null,null,-1),q=n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1607929114683-23cb4794-bc67-4bed-8e95-0a8f493cb1e6.png",alt:"",loading:"lazy"},null,-1),j=n("br",null,null,-1),E={href:"https://ssr.vuejs.org/guide/universal.html#component-lifecycle-hooks",target:"_blank",rel:"noopener noreferrer"},T=t(`

修改如下:

移除未使用的 Javascript

来看下一条优化建议:

因为经过多次迭代,有可能某些功能曾经上线过,后来被下线,但当时代码没删干净,所以留下一些现在没用的第三方库。根据建议,找到这些引入的 js,确保不影响正常功能后,删除即可。

此时注意用到以下基本操作:

  1. google/github 查询库的用途
  2. git history/annotate 查看是何人何时引入、并在何处使用的,如何代码不能表明意图,则最好找到相关人员询问是有何意图。

延迟静态资源的加载

来看另一个相关的建议:

如果有些第三方 js 确实有用到,但却不是关键资源,则可以延迟其加载或解析时机,缩短阻塞时间。

以一些第三方代码为例,它们并不是关键资源,可以在 window.onload 事件触发后,再加载

const srcs = [
+import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o as l,c as p,e as r,a as n,b as a,d as e,f as t}from"./app-a9d55428.js";const c={},d=n("h1",{id:"前端项目性能优化实战",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#前端项目性能优化实战","aria-hidden":"true"},"#"),a(" 前端项目性能优化实战")],-1),u=n("p",null,"本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。",-1),m=n("h2",{id:"检测",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#检测","aria-hidden":"true"},"#"),a(" 检测")],-1),h=n("p",null,"使用两个工具分析项目首页性能情况:",-1),g={href:"https://developers.google.com/speed/pagespeed/insights/",target:"_blank",rel:"noopener noreferrer"},b={href:"https://tools.pingdom.com/",target:"_blank",rel:"noopener noreferrer"},k=n("p",null,[a("得到结果如下:"),n("br"),n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1611714344531-6501362c-526d-40e5-b1a4-a3399a544ea1.png",alt:"",loading:"lazy"}),n("br"),n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1607738629800-10ff9829-ea0c-402b-b826-1d05903ee302.png",alt:"",loading:"lazy"}),n("br"),a(" 可以看到,首页超过50%的请求都与图片有空,优化空间比较大,因此第一步应该是优化图片加载。")],-1),v=n("h2",{id:"图片优化",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#图片优化","aria-hidden":"true"},"#"),a(" 图片优化")],-1),f=n("br",null,null,-1),_=n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1607739432571-c4ebd1bb-bf91-4ced-afca-ad70b84c4de6.png",alt:"",loading:"lazy"},null,-1),y=n("br",null,null,-1),w={href:"https://zhuanlan.zhihu.com/p/99769484",target:"_blank",rel:"noopener noreferrer"},x=t('
  1. <img>  元素修改成 <v-img/> ,注意设置 width 或 height
  2. <div ``style="background-image: url(img-url)"``></div> 修改成 <div v-img="{src: img-url}"></div>

注意:img-url 应该是 oss 的链接,并且是 https 协议。
如果是 http 协议,或不指定协议 //img-url,则很可能会出现下图的情况:

如果图片是放在项目中,且项目并没有部署到 oss,则无法享受自动加载 webp 格式图片的福利。

则在此环节,一次性做到了上图中的三个优化点。

提高TTFB时间

来看下一条优化建议:

因为项目是服务端渲染的,有些请求是在服务端做了。找到相关代码:

经过分析,上述代码存在两个问题:

  1. 可在客户端执行却放在了服务端
  2. 可并行执行却写成了串行

修改如下:


',10),z=n("br",null,null,-1),C=n("br",null,null,-1),q=n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1607929114683-23cb4794-bc67-4bed-8e95-0a8f493cb1e6.png",alt:"",loading:"lazy"},null,-1),j=n("br",null,null,-1),E={href:"https://ssr.vuejs.org/guide/universal.html#component-lifecycle-hooks",target:"_blank",rel:"noopener noreferrer"},T=t(`

修改如下:

移除未使用的 Javascript

来看下一条优化建议:

因为经过多次迭代,有可能某些功能曾经上线过,后来被下线,但当时代码没删干净,所以留下一些现在没用的第三方库。根据建议,找到这些引入的 js,确保不影响正常功能后,删除即可。

此时注意用到以下基本操作:

  1. google/github 查询库的用途
  2. git history/annotate 查看是何人何时引入、并在何处使用的,如何代码不能表明意图,则最好找到相关人员询问是有何意图。

延迟静态资源的加载

来看另一个相关的建议:

如果有些第三方 js 确实有用到,但却不是关键资源,则可以延迟其加载或解析时机,缩短阻塞时间。

以一些第三方代码为例,它们并不是关键资源,可以在 window.onload 事件触发后,再加载

const srcs = [
   // 百度统计
   {
     src: 'https://hm.baidu.com/hm.js',
diff --git a/assets/recommend-practices-for-collections-naming-convention.html-4ce11630.js b/assets/recommend-practices-for-collections-naming-convention.html-4ce11630.js
new file mode 100644
index 00000000..92bbc988
--- /dev/null
+++ b/assets/recommend-practices-for-collections-naming-convention.html-4ce11630.js
@@ -0,0 +1,46 @@
+import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{o as a,c as s,f as e}from"./app-a9d55428.js";const t={},p=e(`

集合命名推荐

概述

建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。

当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章?
image.png

这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。

List

List 的变量,一般以 List 或 s 结尾, 如 idList 或 ids。这点易于理解,大家也容易遵守。

坏的示例:

nodeType.forEach(t -> {
+    // 省略代码
+});
+

第一眼看到这代码的时候,不知道读者是什么反应?

按照习惯,nodeType 通常要么是字符串、数字、或枚举,但上述居然能调用 forEach 方法?我不禁愣了一下,赶紧去看了下定义,才发现原来是List。

好的示例:

nodeTypes.forEach(t -> {
+    
+});
+
+// 或
+nodeTypeList.forEach(t -> {
+    
+});
+

Set

参考List,在变量后面加 Set 即可。

Map

Map 的变量命名是值得重点关注,因为很容易造成差可读性的重灾区。

Map 的变量,推荐根据 key 与 value 来命名。规则表达式为:\${key} + To + \${value} + Map,如 idToNameMap。
其中:

  • 可以玩一下“文字游戏”,把 To 写成 2(就像把 For 写成 4,这种 word play 是可以接受的),即 id2NameMap
  • 如果名字够清晰或已经很长,Map 可以省略,如 id2Name

为什么推荐这样命名?我们先来看一则案例,看一看经典的 Map<String, String> 在实际编码中,命名是如何造成理解上的困难的。

List<TableNode> tableNodes = getFromSomePlace();
+Map<String, String> tableIdMaps = getFromAnotherPlace(); 
+
+// 重点看下面两行代码
+Map<String, String> tableMaps = getTableIdMap(tableNodes, tableIdMaps); 
+replaceNodeId(tableNodes, tableMaps);
+

getTableIdMap核心实现如下:

// 谜之代码
+tableNodes.forEach(tableNode -> {
+    String tableNodeId = getTableNodeId(tableIdMaps);
+    tableMaps.put(tableNode.getNodeId(), tableNodeId);  //???
+});
+
+return tableMaps;
+

replaceNodeId核心实现如下:

// 谜之代码
+tableNodes.forEach(tableNode -> {
+    String nodeId = tableMaps.get(tableNode.getNodeId());  // ???
+    tableNode.setNodeId(nodeId);
+});
+

不知读者是否已经晕了?反正我是一头雾水。可能以为是因为我删减了很多代码导致的?恰恰相反,实际代码还有更多的逻辑判断,我已经抽出了核心部分,不需要被其他逻辑干扰了。

上面的代码带来的疑问有:
疑问1:都是 Map<String, String>tableIdMapstableMaps 有什么区别,它们存储的到底是什么?从类型上看,也不像是 id -> table 的映射啊。
疑问2:tableMaps.put(tableNode.getNodeId(), tableNodeId); 这个 tableNode.getNodeId() 不是等于 tableNodeId吗?
疑问3:String nodeId = tableMaps.get(tableNode.getNodeId()); 根据 nodeId 拿到 nodeId?

到这里已可以猜到,tableMaps里的 key 与 value 肯定不是单纯的 nodeId 的意思,但这并没有什么帮助,因为我们还是不知道 tableMaps key -> value 映射的到底是什么。

我们来看修改变量名之后,上述代码的效果。

List<TableNode> tableNodes = getFromSomePlace();
+//修改下面两个 Map 的命名
+Map<String, String> str2TableIdMap = getFromAnotherPlace(); 
+Map<String, String> nodeId2TableIdMap = getTableIdMap(tableNodes, str2TableIdMap); 
+
+replaceNodeId(tableNodes, nodeId2TableIdMap);
+
tableNodes.forEach(tableNode -> {
+    String tableId = getTableNodeId(str2TableIdMap); // 修改了这行
+    nodeId2TableIdMap.put(tableNode.getNodeId(), tableId); // 修改了这行
+});
+
+return nodeId2TableIdMap;
+
tableNodes.forEach(tableNode -> {
+    String tableId = nodeId2TableIdMap.get(tableNode.getNodeId());  //修改了这行
+    tableNode.setNodeId(tableId);
+});
+

现在是不是好懂很多了:

  • str2TableIdMap 存储的 str -> tableId 的映射, 其中 str 是由某种规则拼接而成的字符串,具体规则封装在了 getTableNodeId这个函数里,我们暂时可以不用关心
  • nodeId2TableIdMap 存储的是 nodeId -> tableId 的映射

仅仅修改变量名,可读性就有大大提高,效果立竿见影!

`,35),o=[p];function c(l,i){return a(),s("div",null,o)}const r=n(t,[["render",c],["__file","recommend-practices-for-collections-naming-convention.html.vue"]]);export{r as default}; diff --git a/assets/recommend-practices-for-collections-naming-convention.html-ba39413b.js b/assets/recommend-practices-for-collections-naming-convention.html-ba39413b.js new file mode 100644 index 00000000..60db2028 --- /dev/null +++ b/assets/recommend-practices-for-collections-naming-convention.html-ba39413b.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-d767e98e","path":"/java/recommend-practices-for-collections-naming-convention.html","title":"集合命名推荐","lang":"zh-CN","frontmatter":{"date":"2022-06-01T00:00:00.000Z","tag":["Java","Daily"],"description":"集合命名推荐 概述 建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。 当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章? 这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/recommend-practices-for-collections-naming-convention.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"集合命名推荐"}],["meta",{"property":"og:description","content":"集合命名推荐 概述 建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。 当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章? 这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-06-01T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"集合命名推荐\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-01T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"概述","slug":"概述","link":"#概述","children":[]},{"level":2,"title":"List","slug":"list","link":"#list","children":[]},{"level":2,"title":"Set","slug":"set","link":"#set","children":[]},{"level":2,"title":"Map","slug":"map","link":"#map","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.02,"words":906},"filePathRelative":"java/recommend-practices-for-collections-naming-convention.md","localizedDate":"2022年6月1日","excerpt":"

集合命名推荐

\\n

概述

\\n

建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。

\\n

当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章?
\\n\\"image.png\\"

\\n

这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。

","autoDesc":true}');export{e as data}; diff --git a/assets/recommend-practices-for-query-by-date-range.html-9cb3e939.js b/assets/recommend-practices-for-query-by-date-range.html-c4cb8fd3.js similarity index 90% rename from assets/recommend-practices-for-query-by-date-range.html-9cb3e939.js rename to assets/recommend-practices-for-query-by-date-range.html-c4cb8fd3.js index a59494e2..b3c5681b 100644 --- a/assets/recommend-practices-for-query-by-date-range.html-9cb3e939.js +++ b/assets/recommend-practices-for-query-by-date-range.html-c4cb8fd3.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-f5471cb0","path":"/java/recommend-practices-for-query-by-date-range.html","title":"根据时间范围查询推荐实践","lang":"zh-CN","frontmatter":{"date":"2023-09-12T00:00:00.000Z","tag":["Java","Daily"],"description":"根据时间范围查询推荐实践 背景 不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。 虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。 本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/recommend-practices-for-query-by-date-range.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"根据时间范围查询推荐实践"}],["meta",{"property":"og:description","content":"根据时间范围查询推荐实践 背景 不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。 虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。 本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-12T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"根据时间范围查询推荐实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-12T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"需求","slug":"需求","link":"#需求","children":[]},{"level":2,"title":"分析","slug":"分析","link":"#分析","children":[]},{"level":2,"title":"实现","slug":"实现","link":"#实现","children":[{"level":3,"title":"MySQL","slug":"mysql","link":"#mysql","children":[]},{"level":3,"title":"MyBatis","slug":"mybatis","link":"#mybatis","children":[]},{"level":3,"title":"LoxalDate","slug":"loxaldate","link":"#loxaldate","children":[]},{"level":3,"title":"Jackson","slug":"jackson","link":"#jackson","children":[]}]},{"level":2,"title":"结语","slug":"结语","link":"#结语","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.21,"words":962},"filePathRelative":"java/recommend-practices-for-query-by-date-range.md","localizedDate":"2023年9月12日","excerpt":"

根据时间范围查询推荐实践

\\n

背景

\\n

不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。

\\n

虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。

\\n

本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-f5471cb0","path":"/java/recommend-practices-for-query-by-date-range.html","title":"根据时间范围查询推荐实践","lang":"zh-CN","frontmatter":{"date":"2023-09-12T00:00:00.000Z","tag":["Java","Daily"],"description":"根据时间范围查询推荐实践 背景 不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。 虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。 本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/recommend-practices-for-query-by-date-range.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"根据时间范围查询推荐实践"}],["meta",{"property":"og:description","content":"根据时间范围查询推荐实践 背景 不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。 虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。 本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-12T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"根据时间范围查询推荐实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-12T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"需求","slug":"需求","link":"#需求","children":[]},{"level":2,"title":"分析","slug":"分析","link":"#分析","children":[]},{"level":2,"title":"实现","slug":"实现","link":"#实现","children":[{"level":3,"title":"MySQL","slug":"mysql","link":"#mysql","children":[]},{"level":3,"title":"MyBatis","slug":"mybatis","link":"#mybatis","children":[]},{"level":3,"title":"LoxalDate","slug":"loxaldate","link":"#loxaldate","children":[]},{"level":3,"title":"Jackson","slug":"jackson","link":"#jackson","children":[]}]},{"level":2,"title":"结语","slug":"结语","link":"#结语","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.21,"words":962},"filePathRelative":"java/recommend-practices-for-query-by-date-range.md","localizedDate":"2023年9月12日","excerpt":"

根据时间范围查询推荐实践

\\n

背景

\\n

不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。

\\n

虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。

\\n

本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/recommend-practices-for-query-by-date-range.html-c58f48b5.js b/assets/recommend-practices-for-query-by-date-range.html-f4c04744.js similarity index 99% rename from assets/recommend-practices-for-query-by-date-range.html-c58f48b5.js rename to assets/recommend-practices-for-query-by-date-range.html-f4c04744.js index ce3bd3e2..7ee14d1c 100644 --- a/assets/recommend-practices-for-query-by-date-range.html-c58f48b5.js +++ b/assets/recommend-practices-for-query-by-date-range.html-f4c04744.js @@ -1,4 +1,4 @@ -import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{o as e,c as t,e as p,a as n,b as a,f as o}from"./app-b649ee34.js";const c={},l=n("h1",{id:"根据时间范围查询推荐实践",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#根据时间范围查询推荐实践","aria-hidden":"true"},"#"),a(" 根据时间范围查询推荐实践")],-1),i=n("h2",{id:"背景",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#背景","aria-hidden":"true"},"#"),a(" 背景")],-1),u=n("p",null,"不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。",-1),r=n("p",null,"虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。",-1),d=n("p",null,"本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson",-1),k=o(`

需求

删除某个时间段以前的日志。类似于消除浏览记录:

分析

上图只是UI示例,为了适应UI的变化,最好把接口设置成接收两个参数:

  • 开始时间
  • 结束时间

如果UI如上图所示,则选择过去 7 天时,开始时间就是 1970 年 1 月 1 日,结束时间就是过去第 7 天。

实现

MySQL

对于 MySQL,推荐使用,因为简单直观,且方便:

SELECT * FROM operation_logs 
+import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{o as e,c as t,e as p,a as n,b as a,f as o}from"./app-a9d55428.js";const c={},l=n("h1",{id:"根据时间范围查询推荐实践",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#根据时间范围查询推荐实践","aria-hidden":"true"},"#"),a(" 根据时间范围查询推荐实践")],-1),i=n("h2",{id:"背景",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#背景","aria-hidden":"true"},"#"),a(" 背景")],-1),u=n("p",null,"不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。",-1),r=n("p",null,"虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。",-1),d=n("p",null,"本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson",-1),k=o(`

需求

删除某个时间段以前的日志。类似于消除浏览记录:

分析

上图只是UI示例,为了适应UI的变化,最好把接口设置成接收两个参数:

  • 开始时间
  • 结束时间

如果UI如上图所示,则选择过去 7 天时,开始时间就是 1970 年 1 月 1 日,结束时间就是过去第 7 天。

实现

MySQL

对于 MySQL,推荐使用,因为简单直观,且方便:

SELECT * FROM operation_logs 
 WHERE created_time BETWEEN '2023-09-12 11:44:26' AND '2023-09-12 13:54:52';
 

另一种方式:

SELECT * FROM operation_logs 
 WHERE created_time >= '2023-09-12 11:44:26' AND created_time <= '2023-09-12 13:54:52';
diff --git a/assets/recommend-practices-for-writing-good-functions.html-263e2d20.js b/assets/recommend-practices-for-writing-good-functions.html-263e2d20.js
new file mode 100644
index 00000000..68662ad3
--- /dev/null
+++ b/assets/recommend-practices-for-writing-good-functions.html-263e2d20.js
@@ -0,0 +1,62 @@
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as c,c as l,a as n,b as a,d as e,f as p}from"./app-a9d55428.js";const i={},u=p('

编写函数的最佳实践

前言

编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。

本文将推荐一些编写函数的最佳实践,以供参数。

减少重复

',5),d={href:"https://en.wikipedia.org/wiki/Don%27t_repeat_yourself",target:"_blank",rel:"noopener noreferrer"},r=p(`

实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。

然而,有时代码只是相似,不完全相同,不能简单地使用 IDEA 右键 + Refactor + Extract Method 来抽取函数。
此时,为减少重复,需要进行一些思考。

可以把程序的划分成三个部分:

Program = Control + Logic + Data Structure
+

一般而言,函数的入参都是数据变量,也即 Data Structure。
而 Java 8 以后,lambda 表达式(也即函数)可以作为入参,其代表的是 Logic。
因此,最抽象的函数,是只定义了 Control、把 Logic 及 Data Structure 都作为入参的函数。当遇到类似却不完全相同的代码、想封装函数有遇难时,可以借助上述思路来梳理逻辑。

隐藏细节

隐藏细节,是为了减少使用者的心智负担,方便其调用。

有一个简单的判断标准:如果调用者需要频繁查看函数内部情况,以确定函数的目的或实现细节,那么隐藏细节的意图是失败的。

建议

为了达到前文所述的目的,如以下实践建议。需要指出的是,以下提倡的是建议,并非金科玉律;只适用于一般情况,并非所有情况,特殊情况是可以特殊处理的。

优先根据业务命名

一般而言,函数名最好是根据业务逻辑、结合业务领域来命名,而不是根据程序逻辑来命名。

示例:

// bad
+String getString(UserDTO user);
+
+// good
+String getUsername(UserDTO user);
+

当然,如果有些方法名是专业名词或是耳熟能详的,那直接使用即可,如:

void bfs();
+
+void shortestPath();
+
+void binarySearch();
+

一个函数只做一件事

`,17),k={href:"https://en.wikipedia.org/wiki/KISS_principle",target:"_blank",rel:"noopener noreferrer"},m=p(`

当然,不可能所有函数都达到这个要求——程序入口一般就会做很多事。我们的目标是尽可能地遵守该原则,减少调用者需要频繁查看函数实现的可能。

反例1:做A且做B

// bad
+int doSomethingAndAnother(Param param);
+
+// good 
+// 拆分成两个函数
+int doSomething(ParamA a);
+int doAnother(ParamB b);
+

坏的示例问题出在哪里呢?根据入参的合法性,有可能产生以下情况:

  1. 参数合法,同时做A与B;只要有一个参数不合法,均不做A与B
  2. 哪个合法就做哪个,也即可能出现:
    1. 只做A
    2. 只做B
    3. 做A也做B
    4. 二者均不做

到底是什么情况呢?对此疑问,调用方只有查看函数实现,才能了解,于是破坏了封装的意图。
而且,坏的示例还会存在一个问题:如果调用方只想做A怎么办?我想很少人会把原代码拆分成两个函数,更常见的做法是保持原函数不变,并拷贝原函数的部分逻辑,封装一个只做A的新函数——这就造成了代码的冗余,于是减少重复的目的失败了。

反例2:做A或做B

// bad
+int doSomethingOrAnother(Param param);
+
+// good 
+// 拆分成两个函数
+int doSomething(ParamA a);
+int doAnother(ParamB b);
+

同样的,坏的示例会让人疑惑,搞不清楚函数的意图到底属于以下哪种情况:

  1. 要么做A,要么做B,一定会做其中一个
  2. 哪个合法做哪个,可能会出现:
    1. 只做A
    2. 只做B
    3. 做A也做B
    4. 二者均不做

当然,一些常见的深入人心的 API,我们是可以接受这种“或逻辑”的:

  • saveOrUpdate() // 有 id 就是 update,没有就是 insert
  • getOrDefault() // 获取值;如果值不存在,就返回默认值

优先使用纯函数

纯函数(pure function),可以借助数学中的函数概念来理解:y = f(x)

  • 给定 x,能返回确定的 y
  • 无论函数调用几次、在何处调用,上述结果都不会变化

纯函数的好处之一是无副作用(side effect)。也即调用函数后,不会对函数作用域以外的变量造成影响。

反例:一个常见的现象,辅助函数会修改入参,主函数的变量生命周期贯穿整个主函数

Result getRelation(String nodeId, Param param) {
+    // 省略其他代码
+    
+    Map<Long, Node> nodesMap = new HashMap<>();   
+
+    // 没有返回值
+    getUpstream(nodeId, nodesMap, param);
+
+    // “废物利用”!
+    nodesMap.clear();
+
+    // 没有返回值
+    getDownstream(nodeId, nodesMap, param);
+  
+    Map<Long, NodeDTO> nodesDtoMap = new HashMap<>();
+    return getResult(nodesDtoMap, nodesMap);
+} 
+

因为已经省略了其他代码,因此我们不难看出 nodesMap是辅助变量,是为返回结果而服务的,而没有返回值的函数调用很可能修改了该变量。

但实际上,代码逻辑很长,还有其他变量掺杂其中,代码意图并非能够一目了然。假设稍微修改一下,为 getUpstream()添加多一个参数,还能看出函数到底修改了哪个变量吗?

Map<Long, Node> nodesMap = new HashMap<>(); 
+// 添加多一个变量
+Map<Long, Edge> edgesMap = new HashMap<>();
+
+getUpstream(nodeId, nodesMap, edgesMap, param);
+

如果 edgesMap是来自主函数的参数呢?

Result getRelation(String nodeId, Map<Long, Edge> edgesMap, Param param) {
+    // 省略其他代码
+    
+    Map<Long, Node> nodesMap = new HashMap<>();   
+
+    // edgesMap 是主函数传参
+    getUpstream(nodeId, nodesMap, edgesMap, param);
+       
+    // 省略其他代码 
+} 
+

情况变得糟糕了,因为按照 getUpstream()会修改入参的“习性”,我们很难有信心认为 edgesMap一定没有被修改。

上述例子是想表明:为了贪图方便,编写一个不需要返回值而直接修改入参的函数,会给后续的维护增加负担。一方面变量状态难以追踪,另一方面这样的函数也不方便测试。

一般而言,优先使用纯函数,会助于对大函数的拆分,从而使得 KISS 原则更容易被遵守。

当然,总有例外情况。当程序逻辑复杂时,或有些函数就是对 setter 语句的调用,此时不需要返回值并且会造成副作用,又该怎么办呢?请看下一条建议。

编写不需要返回值的函数

有如下建议:

`,29),v=n("ol",null,[n("li",{要修改的变量名:""},[a("方法名叫"),n("code",null,"setup"),a("+ $")]),n("li",null,"一个方法只修改一个变量"),n("li",null,"要修改的变量就是函数的第一个参数"),n("li",null,"lambda(如果有的话) 作为最后的参数")],-1),g=p(`

示例:

setupNodeCommonInfo(node, nodeId, queryUpstream);
+setupNodeTableInfo(node, nodeId2Table);
+
+setupEdgeCrossLayer(edges, Edge::getSourceId, Edge::getTargetId);
+

参考资料

`,3),b={href:"https://mostly-adequate.gitbook.io/mostly-adequate-guide/ch03",target:"_blank",rel:"noopener noreferrer"},h={href:"http://aroma.vn/web/wp-content/uploads/2016/11/code-complete-2nd-edition-v413hav.pdf",target:"_blank",rel:"noopener noreferrer"};function f(_,M){const s=o("ExternalLinkIcon");return c(),l("div",null,[u,n("p",null,[a("这是在遵守 "),n("a",d,[a("Don't repeat yourself"),e(s)]),a(" (DRY) 原则。")]),r,n("p",null,[a("遵守 "),n("a",k,[a("Keep it simple stupid"),e(s)]),a(" (KISS) 原则。")]),m,v,g,n("ul",null,[n("li",null,[n("a",b,[a("functional-programming-mostly-adequate-guide/ch03"),e(s)])]),n("li",null,[n("a",h,[a("code-complete-2nd-edition.pdf"),e(s)])])])])}const j=t(i,[["render",f],["__file","recommend-practices-for-writing-good-functions.html.vue"]]);export{j as default}; diff --git a/assets/recommend-practices-for-writing-good-functions.html-6cc4ed1e.js b/assets/recommend-practices-for-writing-good-functions.html-6cc4ed1e.js new file mode 100644 index 00000000..30749d80 --- /dev/null +++ b/assets/recommend-practices-for-writing-good-functions.html-6cc4ed1e.js @@ -0,0 +1 @@ +const e=JSON.parse(`{"key":"v-57d9b582","path":"/java/recommend-practices-for-writing-good-functions.html","title":"编写函数的最佳实践","lang":"zh-CN","frontmatter":{"date":"2022-10-14T00:00:00.000Z","tag":["Java","Daily"],"description":"编写函数的最佳实践 前言 编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。 本文将推荐一些编写函数的最佳实践,以供参数。 减少重复 这是在遵守 Don't repeat yourself (DRY) 原则。 实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/recommend-practices-for-writing-good-functions.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"编写函数的最佳实践"}],["meta",{"property":"og:description","content":"编写函数的最佳实践 前言 编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。 本文将推荐一些编写函数的最佳实践,以供参数。 减少重复 这是在遵守 Don't repeat yourself (DRY) 原则。 实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-10-14T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"编写函数的最佳实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-10-14T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[{"level":3,"title":"减少重复","slug":"减少重复","link":"#减少重复","children":[]},{"level":3,"title":"隐藏细节","slug":"隐藏细节","link":"#隐藏细节","children":[]}]},{"level":2,"title":"建议","slug":"建议","link":"#建议","children":[{"level":3,"title":"优先根据业务命名","slug":"优先根据业务命名","link":"#优先根据业务命名","children":[]},{"level":3,"title":"一个函数只做一件事","slug":"一个函数只做一件事","link":"#一个函数只做一件事","children":[]},{"level":3,"title":"优先使用纯函数","slug":"优先使用纯函数","link":"#优先使用纯函数","children":[]},{"level":3,"title":"编写不需要返回值的函数","slug":"编写不需要返回值的函数","link":"#编写不需要返回值的函数","children":[]}]},{"level":2,"title":"参考资料","slug":"参考资料","link":"#参考资料","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5.88,"words":1764},"filePathRelative":"java/recommend-practices-for-writing-good-functions.md","localizedDate":"2022年10月14日","excerpt":"

编写函数的最佳实践

\\n

前言

\\n

编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。

\\n

本文将推荐一些编写函数的最佳实践,以供参数。

\\n

减少重复

\\n

这是在遵守 Don't repeat yourself (DRY) 原则。

\\n

实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。

","autoDesc":true}`);export{e as data}; diff --git a/assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-32094be4.js b/assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-49fb2b4d.js similarity index 81% rename from assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-32094be4.js rename to assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-49fb2b4d.js index 195b1b87..02f37b6b 100644 --- a/assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-32094be4.js +++ b/assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-49fb2b4d.js @@ -1 +1 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as c,o as a,c as n,e as r,d as s,a as e,b as i}from"./app-b649ee34.js";const l={},_=e("h1",{id:"微软中国cto演讲观后感",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#微软中国cto演讲观后感","aria-hidden":"true"},"#"),i(" 微软中国CTO演讲观后感")],-1),d=e("p",null,"看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。",-1);function f(h,m){const o=c("BiliBili");return a(),n("div",null,[_,d,r(" more "),s(o,{bvid:"BV1E14y1C72L"})])}const u=t(l,[["render",f],["__file","reflections-on-a-speech-by-cto-of-microsoft-china.html.vue"]]);export{u as default}; +import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as c,o as a,c as n,e as r,d as s,a as e,b as i}from"./app-a9d55428.js";const l={},_=e("h1",{id:"微软中国cto演讲观后感",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#微软中国cto演讲观后感","aria-hidden":"true"},"#"),i(" 微软中国CTO演讲观后感")],-1),d=e("p",null,"看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。",-1);function f(h,m){const o=c("BiliBili");return a(),n("div",null,[_,d,r(" more "),s(o,{bvid:"BV1E14y1C72L"})])}const u=t(l,[["render",f],["__file","reflections-on-a-speech-by-cto-of-microsoft-china.html.vue"]]);export{u as default}; diff --git a/assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-51d46984.js b/assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-66a9c3f3.js similarity index 85% rename from assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-51d46984.js rename to assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-66a9c3f3.js index d53a8203..53ffd786 100644 --- a/assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-51d46984.js +++ b/assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-66a9c3f3.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-5c48d497","path":"/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html","title":"微软中国CTO演讲观后感","lang":"zh-CN","frontmatter":{"date":"2023-09-10T00:00:00.000Z","tag":["Daily","Video"],"description":"微软中国CTO演讲观后感 看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"微软中国CTO演讲观后感"}],["meta",{"property":"og:description","content":"微软中国CTO演讲观后感 看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-09-10T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"微软中国CTO演讲观后感\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-10T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.17,"words":51},"filePathRelative":"daily/reflections-on-a-speech-by-cto-of-microsoft-china.md","localizedDate":"2023年9月10日","excerpt":"

微软中国CTO演讲观后感

\\n

看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-5c48d497","path":"/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html","title":"微软中国CTO演讲观后感","lang":"zh-CN","frontmatter":{"date":"2023-09-10T00:00:00.000Z","tag":["Daily","Video"],"description":"微软中国CTO演讲观后感 看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"微软中国CTO演讲观后感"}],["meta",{"property":"og:description","content":"微软中国CTO演讲观后感 看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-09-10T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"微软中国CTO演讲观后感\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-10T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.17,"words":51},"filePathRelative":"daily/reflections-on-a-speech-by-cto-of-microsoft-china.md","localizedDate":"2023年9月10日","excerpt":"

微软中国CTO演讲观后感

\\n

看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/rethinking-git-flow.html-39a67167.js b/assets/rethinking-git-flow.html-60cc8caf.js similarity index 90% rename from assets/rethinking-git-flow.html-39a67167.js rename to assets/rethinking-git-flow.html-60cc8caf.js index 4c504496..5b75fb11 100644 --- a/assets/rethinking-git-flow.html-39a67167.js +++ b/assets/rethinking-git-flow.html-60cc8caf.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-0fbf5fdc","path":"/git/rethinking-git-flow.html","title":"再论Git Flow","lang":"zh-CN","frontmatter":{"date":"2022-04-21T00:00:00.000Z","tag":"Git","description":"再论Git Flow 背景 团队目前使用的 Git 协作模式是: 对每个功能建立相应的 feat 分支 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支 这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/rethinking-git-flow.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"再论Git Flow"}],["meta",{"property":"og:description","content":"再论Git Flow 背景 团队目前使用的 Git 协作模式是: 对每个功能建立相应的 feat 分支 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支 这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2022-04-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"再论Git Flow\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-04-21T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"动机","slug":"动机","link":"#动机","children":[]},{"level":2,"title":"分析","slug":"分析","link":"#分析","children":[{"level":3,"title":"剔除代码","slug":"剔除代码","link":"#剔除代码","children":[]},{"level":3,"title":"再次提交","slug":"再次提交","link":"#再次提交","children":[]},{"level":3,"title":"比较优劣","slug":"比较优劣","link":"#比较优劣","children":[]}]},{"level":2,"title":"实例","slug":"实例","link":"#实例","children":[{"level":3,"title":"分支模型","slug":"分支模型","link":"#分支模型","children":[]},{"level":3,"title":"功能提交","slug":"功能提交","link":"#功能提交","children":[]},{"level":3,"title":"功能回撤","slug":"功能回撤","link":"#功能回撤","children":[]}]},{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5.53,"words":1660},"filePathRelative":"git/rethinking-git-flow.md","localizedDate":"2022年4月21日","excerpt":"

再论Git Flow

\\n

背景

\\n

团队目前使用的 Git 协作模式是:

\\n
    \\n
  1. 对每个功能建立相应的 feat 分支
  2. \\n
  3. 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat
  4. \\n
  5. 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支
  6. \\n
\\n

这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-0fbf5fdc","path":"/git/rethinking-git-flow.html","title":"再论Git Flow","lang":"zh-CN","frontmatter":{"date":"2022-04-21T00:00:00.000Z","tag":"Git","description":"再论Git Flow 背景 团队目前使用的 Git 协作模式是: 对每个功能建立相应的 feat 分支 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支 这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/rethinking-git-flow.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"再论Git Flow"}],["meta",{"property":"og:description","content":"再论Git Flow 背景 团队目前使用的 Git 协作模式是: 对每个功能建立相应的 feat 分支 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支 这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2022-04-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"再论Git Flow\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-04-21T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"动机","slug":"动机","link":"#动机","children":[]},{"level":2,"title":"分析","slug":"分析","link":"#分析","children":[{"level":3,"title":"剔除代码","slug":"剔除代码","link":"#剔除代码","children":[]},{"level":3,"title":"再次提交","slug":"再次提交","link":"#再次提交","children":[]},{"level":3,"title":"比较优劣","slug":"比较优劣","link":"#比较优劣","children":[]}]},{"level":2,"title":"实例","slug":"实例","link":"#实例","children":[{"level":3,"title":"分支模型","slug":"分支模型","link":"#分支模型","children":[]},{"level":3,"title":"功能提交","slug":"功能提交","link":"#功能提交","children":[]},{"level":3,"title":"功能回撤","slug":"功能回撤","link":"#功能回撤","children":[]}]},{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5.53,"words":1660},"filePathRelative":"git/rethinking-git-flow.md","localizedDate":"2022年4月21日","excerpt":"

再论Git Flow

\\n

背景

\\n

团队目前使用的 Git 协作模式是:

\\n
    \\n
  1. 对每个功能建立相应的 feat 分支
  2. \\n
  3. 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat
  4. \\n
  5. 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支
  6. \\n
\\n

这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:

","autoDesc":true}');export{e as data}; diff --git a/assets/rethinking-git-flow.html-2dbc1f93.js b/assets/rethinking-git-flow.html-cd22e8fc.js similarity index 99% rename from assets/rethinking-git-flow.html-2dbc1f93.js rename to assets/rethinking-git-flow.html-cd22e8fc.js index ce615dab..f32265d6 100644 --- a/assets/rethinking-git-flow.html-2dbc1f93.js +++ b/assets/rethinking-git-flow.html-cd22e8fc.js @@ -1 +1 @@ -import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as h,c,a,b as e,d as i,f as t}from"./app-b649ee34.js";const d={},n=t('

再论Git Flow

背景

团队目前使用的 Git 协作模式是:

  1. 对每个功能建立相应的 feat 分支
  2. 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat
  3. 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支

这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:

  1. 某 feat 合并至 dev 后,并不想合并至 test
  2. 某 feat 合并至 test 后,并不想合并至 uat

本文暂且不讨论该交付理念的优劣,毕竟每个团队研发情况、交付理念都不一样。 本文关注的是,在满足上述需求的情况下,是否有更好的分支协作方式。

动机

为什么要寻求更好的方式?因为上述分支协作模式,会导致代码冲突的噩梦:

  1. feat -> dev,解决冲突
  2. feat -> test,又要解决冲突
  3. feat -> uat,还要解决冲突
',10),p={href:"https://www.cloudbees.com/blog/pitfalls-feature-branching",target:"_blank",rel:"noopener noreferrer"},s=t('

再者,功能已经通过测试了,准备上UAT环境时,居然还要解决一大堆曾经解决过的冲突,实在不想接受这种“惊喜”(或者说“惊吓”更合适)——合错代码了怎么办?并且,这么多分支,遗漏了怎么办?这些问题可以解决,但难免有为了解决一个问题,引入更多问题之嫌。

理想中的研发流程是,测试通过后,上 UAT 的体验是平滑的,是不用担心出错的。

为此,本文思考是否存在另一种分支协作的方式。

分析

首先分析一下,“某功能测试通过但不上 UAT”的可操作方法有哪些:

  1. 要上 UAT 的 feat 分支逐个依次合并至长驻分支,也即当前的做法
  2. 在原计划要上的功能的代码集合中,剔除掉相应 feat 的代码,再上 UAT
  3. 相应分支再次提交代码,或提交 revert commit,或屏蔽相应的功能及入口,变相达到目的

剔除代码

先来看第2种方法。filter by branch,这是最先想到且符合直觉的方式,可惜实际上 Git 并没有此功能。

',8),f={href:"https://www.clock.co.uk/insight/deleting-a-git-commit",target:"_blank",rel:"noopener noreferrer"},u=t('
  1. git rebase
  2. git cherry-pick

git rebase要求 commit 是连续的,这对于实际不可行,因为集成分支里各个 feat 分支的提交记录掺杂在一起。

git cherry-pick是可行的。不过其思路是挑捡想要的 commit,放到目标分支,本质上并不是剔除的逻辑。

再次提交

真正的剔除逻辑,存在于第3种方法中。提交一个 revert commit,就可以把之前的代码干掉了(如果想恢复代码,需要 revert "revert commit")。

觉得 revert 可能会对后续恢复代码造成困扰的话,也可以再提交代码,屏蔽相应功能及入口。这种方式适合于功能入口少,功能本身具有类似开关特性的场景。

比较优劣

要比较上述方案优劣,本文倾向于使用功利主义的最佳实践作为指导思想——认为痛苦存在更多共同点,因而为避免消极而努力。换言之,本文关注的是,哪种方案最令人痛苦,则优先淘汰它。

还有一个指导思想:麻烦、辛苦的事情放前面;前面可以多做,后面期望少做。

当前的方式,存在最难受的问题:解决过的冲突,需要重复地解决。涉及范围:全部分支。涉及人员:所有参与研发的人员,即使他们在别的 feat 分支提交代码。

cherry-pick 依然存在要重复解决冲突的问题,且涉及范围同样为全部分支,但涉及人员减少为单人,因为只需要一个做 cherry-pick 的工作,由其解决 cherry-pick 遇到的冲突(当然,很可能需要他人协助)。

再次提交,冲突的可能性将大大减少,涉及范围:相应的 feat 分支。涉及人员:相应的 feat 分支研发人员。

也即使用再次提交的方案,痛苦将降低至最小。这也是符合直觉的:谁出问题,谁负责。某功能不上线了,这也算是“问题”的一种,则相应的负责人去处理,尽可能不影响到其他人。

至于再次提交是使用 revert 还是屏蔽功能及入口,则具体情况具体分析。

实例

下面举例说明,如何应用上述分析结果。

分支模型

长驻分支: dev/test/uat,分别对应环境:研发/测试/UAT

一个月一次的迭代开始时,都建立相应的  release 分支,命名规则可以:

  • 按版本,如: release/v2.15
  • 按上线日期,如:release/04-26

功能提交

每个研发人员根据相应功能,从 uat checkout 相应的 feat 分支。

每次需要集成发布时,正常的分支合并操作如下:

  1. feat -> dev
  2. feat -> release
  3. release -> test
  4. release -> uat

则冲突大多数情况只发生在第前两步,解决之后,后续上测试环境、上 UAT 环境,基本无需担心冲突。

为什么一个 feat 要合并两次?

因为要保证 release 的功能是较为完整的, 至少经过开发人员在 dev 环境的自测。

并且这样也能适应不同的功能分批提测的研发节奏。

功能回撤

当 release 合并至 test 分支后,得到通知,某功能(分支涉及 feat/unwanted)不上 UAT。

则此时,feat/unwanted 相应的研发人员,为了进行功能回滚,操作如下:

  1. checkout rollback 分支
  2. 进行回滚提交,或 revert,或屏蔽功能入口
  3. 请求合并至 UAT(不合并至 release/分支):

后续要恢复功能,在 rollback 分支操作,再合并至 UAT 即可。

结论

通过分析与比较,本文推荐使用“再次提交”的方式,来满足某 feat 分支合进 test,不合进 uat 分支的需求。

这样做,将改动涉及范围减至最小,涉及人员降为单人,大大减少上 UAT 时合并代码的痛苦,达到平稳上线的目的。

',36);function _(b,g){const r=o("ExternalLinkIcon");return h(),c("div",null,[n,a("p",null,[e("正如"),a("a",p,[e("此文章"),i(r)]),e("所说,“把时间浪费在解决不必要的冲突上”。")]),s,a("p",null,[e("想“剔除某 feat 分支的代码”,可操作方式如下,更多请"),a("a",f,[e("参考此文章"),i(r)]),e(":")]),u])}const v=l(d,[["render",_],["__file","rethinking-git-flow.html.vue"]]);export{v as default}; +import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as h,c,a,b as e,d as i,f as t}from"./app-a9d55428.js";const d={},n=t('

再论Git Flow

背景

团队目前使用的 Git 协作模式是:

  1. 对每个功能建立相应的 feat 分支
  2. 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat
  3. 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支

这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:

  1. 某 feat 合并至 dev 后,并不想合并至 test
  2. 某 feat 合并至 test 后,并不想合并至 uat

本文暂且不讨论该交付理念的优劣,毕竟每个团队研发情况、交付理念都不一样。 本文关注的是,在满足上述需求的情况下,是否有更好的分支协作方式。

动机

为什么要寻求更好的方式?因为上述分支协作模式,会导致代码冲突的噩梦:

  1. feat -> dev,解决冲突
  2. feat -> test,又要解决冲突
  3. feat -> uat,还要解决冲突
',10),p={href:"https://www.cloudbees.com/blog/pitfalls-feature-branching",target:"_blank",rel:"noopener noreferrer"},s=t('

再者,功能已经通过测试了,准备上UAT环境时,居然还要解决一大堆曾经解决过的冲突,实在不想接受这种“惊喜”(或者说“惊吓”更合适)——合错代码了怎么办?并且,这么多分支,遗漏了怎么办?这些问题可以解决,但难免有为了解决一个问题,引入更多问题之嫌。

理想中的研发流程是,测试通过后,上 UAT 的体验是平滑的,是不用担心出错的。

为此,本文思考是否存在另一种分支协作的方式。

分析

首先分析一下,“某功能测试通过但不上 UAT”的可操作方法有哪些:

  1. 要上 UAT 的 feat 分支逐个依次合并至长驻分支,也即当前的做法
  2. 在原计划要上的功能的代码集合中,剔除掉相应 feat 的代码,再上 UAT
  3. 相应分支再次提交代码,或提交 revert commit,或屏蔽相应的功能及入口,变相达到目的

剔除代码

先来看第2种方法。filter by branch,这是最先想到且符合直觉的方式,可惜实际上 Git 并没有此功能。

',8),f={href:"https://www.clock.co.uk/insight/deleting-a-git-commit",target:"_blank",rel:"noopener noreferrer"},u=t('
  1. git rebase
  2. git cherry-pick

git rebase要求 commit 是连续的,这对于实际不可行,因为集成分支里各个 feat 分支的提交记录掺杂在一起。

git cherry-pick是可行的。不过其思路是挑捡想要的 commit,放到目标分支,本质上并不是剔除的逻辑。

再次提交

真正的剔除逻辑,存在于第3种方法中。提交一个 revert commit,就可以把之前的代码干掉了(如果想恢复代码,需要 revert "revert commit")。

觉得 revert 可能会对后续恢复代码造成困扰的话,也可以再提交代码,屏蔽相应功能及入口。这种方式适合于功能入口少,功能本身具有类似开关特性的场景。

比较优劣

要比较上述方案优劣,本文倾向于使用功利主义的最佳实践作为指导思想——认为痛苦存在更多共同点,因而为避免消极而努力。换言之,本文关注的是,哪种方案最令人痛苦,则优先淘汰它。

还有一个指导思想:麻烦、辛苦的事情放前面;前面可以多做,后面期望少做。

当前的方式,存在最难受的问题:解决过的冲突,需要重复地解决。涉及范围:全部分支。涉及人员:所有参与研发的人员,即使他们在别的 feat 分支提交代码。

cherry-pick 依然存在要重复解决冲突的问题,且涉及范围同样为全部分支,但涉及人员减少为单人,因为只需要一个做 cherry-pick 的工作,由其解决 cherry-pick 遇到的冲突(当然,很可能需要他人协助)。

再次提交,冲突的可能性将大大减少,涉及范围:相应的 feat 分支。涉及人员:相应的 feat 分支研发人员。

也即使用再次提交的方案,痛苦将降低至最小。这也是符合直觉的:谁出问题,谁负责。某功能不上线了,这也算是“问题”的一种,则相应的负责人去处理,尽可能不影响到其他人。

至于再次提交是使用 revert 还是屏蔽功能及入口,则具体情况具体分析。

实例

下面举例说明,如何应用上述分析结果。

分支模型

长驻分支: dev/test/uat,分别对应环境:研发/测试/UAT

一个月一次的迭代开始时,都建立相应的  release 分支,命名规则可以:

  • 按版本,如: release/v2.15
  • 按上线日期,如:release/04-26

功能提交

每个研发人员根据相应功能,从 uat checkout 相应的 feat 分支。

每次需要集成发布时,正常的分支合并操作如下:

  1. feat -> dev
  2. feat -> release
  3. release -> test
  4. release -> uat

则冲突大多数情况只发生在第前两步,解决之后,后续上测试环境、上 UAT 环境,基本无需担心冲突。

为什么一个 feat 要合并两次?

因为要保证 release 的功能是较为完整的, 至少经过开发人员在 dev 环境的自测。

并且这样也能适应不同的功能分批提测的研发节奏。

功能回撤

当 release 合并至 test 分支后,得到通知,某功能(分支涉及 feat/unwanted)不上 UAT。

则此时,feat/unwanted 相应的研发人员,为了进行功能回滚,操作如下:

  1. checkout rollback 分支
  2. 进行回滚提交,或 revert,或屏蔽功能入口
  3. 请求合并至 UAT(不合并至 release/分支):

后续要恢复功能,在 rollback 分支操作,再合并至 UAT 即可。

结论

通过分析与比较,本文推荐使用“再次提交”的方式,来满足某 feat 分支合进 test,不合进 uat 分支的需求。

这样做,将改动涉及范围减至最小,涉及人员降为单人,大大减少上 UAT 时合并代码的痛苦,达到平稳上线的目的。

',36);function _(b,g){const r=o("ExternalLinkIcon");return h(),c("div",null,[n,a("p",null,[e("正如"),a("a",p,[e("此文章"),i(r)]),e("所说,“把时间浪费在解决不必要的冲突上”。")]),s,a("p",null,[e("想“剔除某 feat 分支的代码”,可操作方式如下,更多请"),a("a",f,[e("参考此文章"),i(r)]),e(":")]),u])}const v=l(d,[["render",_],["__file","rethinking-git-flow.html.vue"]]);export{v as default}; diff --git a/assets/testing-environments-should-be-consistent-with-production-environments.html-009cb1c4.js b/assets/testing-environments-should-be-consistent-with-production-environments.html-009cb1c4.js new file mode 100644 index 00000000..95421bee --- /dev/null +++ b/assets/testing-environments-should-be-consistent-with-production-environments.html-009cb1c4.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-4f919602","path":"/daily/testing-environments-should-be-consistent-with-production-environments.html","title":"生产教训:测试环境要与生产环境一致","lang":"zh-CN","frontmatter":{"date":"2023-11-12T00:00:00.000Z","tag":["Daily"],"description":"生产教训:测试环境要与生产环境一致 事件还原 业务流程: app-a 上传文件 app-b 下载文件后使用文件 其他信息: 开发、测试环境使用 MinIO 生产环境使用 Amazon S3 问题: app-a 上传文件成功 app-b 使用文件报错 逐步分析定位问题: app-a 与 app-b 配置是否一致?——确认都是使用 S3 S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题 app-a 是否真的上传成功?——确认文件已在 S3 app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/testing-environments-should-be-consistent-with-production-environments.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"生产教训:测试环境要与生产环境一致"}],["meta",{"property":"og:description","content":"生产教训:测试环境要与生产环境一致 事件还原 业务流程: app-a 上传文件 app-b 下载文件后使用文件 其他信息: 开发、测试环境使用 MinIO 生产环境使用 Amazon S3 问题: app-a 上传文件成功 app-b 使用文件报错 逐步分析定位问题: app-a 与 app-b 配置是否一致?——确认都是使用 S3 S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题 app-a 是否真的上传成功?——确认文件已在 S3 app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-11-12T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"生产教训:测试环境要与生产环境一致\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-11-12T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"事件还原","slug":"事件还原","link":"#事件还原","children":[]},{"level":2,"title":"分析","slug":"分析","link":"#分析","children":[]},{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.49,"words":748},"filePathRelative":"daily/testing-environments-should-be-consistent-with-production-environments.md","localizedDate":"2023年11月12日","excerpt":"

生产教训:测试环境要与生产环境一致

\\n

事件还原

\\n

业务流程:

\\n
    \\n
  1. app-a 上传文件
  2. \\n
  3. app-b 下载文件后使用文件
  4. \\n
\\n

其他信息:

\\n
    \\n
  1. 开发、测试环境使用 MinIO
  2. \\n
  3. 生产环境使用 Amazon S3
  4. \\n
\\n

问题:

\\n
    \\n
  1. app-a 上传文件成功
  2. \\n
  3. app-b 使用文件报错
  4. \\n
\\n

逐步分析定位问题:

\\n
    \\n
  1. app-a 与 app-b 配置是否一致?——确认都是使用 S3
  2. \\n
  3. S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题
  4. \\n
  5. app-a 是否真的上传成功?——确认文件已在 S3
  6. \\n
  7. app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。
  8. \\n
","autoDesc":true}');export{e as data}; diff --git a/assets/testing-environments-should-be-consistent-with-production-environments.html-292c2a25.js b/assets/testing-environments-should-be-consistent-with-production-environments.html-292c2a25.js new file mode 100644 index 00000000..fc5a22d5 --- /dev/null +++ b/assets/testing-environments-should-be-consistent-with-production-environments.html-292c2a25.js @@ -0,0 +1,2 @@ +import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{o as i,c as e,f as n}from"./app-a9d55428.js";const t={},l=n(`

生产教训:测试环境要与生产环境一致

事件还原

业务流程:

  1. app-a 上传文件
  2. app-b 下载文件后使用文件

其他信息:

  1. 开发、测试环境使用 MinIO
  2. 生产环境使用 Amazon S3

问题:

  1. app-a 上传文件成功
  2. app-b 使用文件报错

逐步分析定位问题:

  1. app-a 与 app-b 配置是否一致?——确认都是使用 S3
  2. S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题
  3. app-a 是否真的上传成功?——确认文件已在 S3
  4. app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。

所以问题就在于,为什么 app-b 的代码会判断文件不存在?

  • 是传过去的路径参数不对?
  • 还是 app-b 的逻辑有问题?

经过确认,传参是正确的,那么只有一个推论: app-b 判断文件是否存在的逻辑有问题。

具体是哪里有问题呢?

原来文件下载前,有一个判断目录是否存在的逻辑,其实现是判断 S3 的对象是否存在,示例代码如下:

return s3Client.doesObjectExist("/path/dir")
+

则该代码永远为 false。

如何修复呢?改为调用 listObjects就可以了。如下图所示(左边是修改前,右边是修改后):
image.png

分析

很难想像,为什么在涉及对象存储的代码中,会有判断目录是否存在的逻辑。

好在我是个善于为别人找理由的人。我观察了下代码库,发现接口与类结构如下:
应该是在写 HDFSStorage 的时候,用到了判断目录是否存在的逻辑,从而把方法提升到了接口定义 IStorage 中。

如果定义 IStorage 与实现 S3Storage 的代码的人并不相同(我相信很可能是这样),那么我更倾向于认为责任在定义 IStorage 的人身上,因为TA并没有合理地设计接口,导致后来者被迫实现没有意义的接口,从而出错。

结论

为什么开发、测试环境没问题?或者说,为什么 MinIO 没这个问题?那是因为实现 MinIOStorage 的人,没有踩这个坑。

所以,本此事件给我来的经验教训是什么呢?既不是 S3 如何判断目录是否存在,也不是接口定义的重要性。而是:要让测试环境与生产环境尽可能保持一致,提前暴露问题。而不是测试环境是这样的配置,生产环境又是那样的配置——这就会加大生产环境出问题的可能性!

`,25),p=[l];function s(d,o){return i(),e("div",null,p)}const h=a(t,[["render",s],["__file","testing-environments-should-be-consistent-with-production-environments.html.vue"]]);export{h as default}; diff --git a/assets/things-I-have-to-vent-about-vue.html-862aa8df.js b/assets/things-I-have-to-vent-about-vue.html-44f6a97e.js similarity index 93% rename from assets/things-I-have-to-vent-about-vue.html-862aa8df.js rename to assets/things-I-have-to-vent-about-vue.html-44f6a97e.js index 0e4d47c0..a6cc8811 100644 --- a/assets/things-I-have-to-vent-about-vue.html-862aa8df.js +++ b/assets/things-I-have-to-vent-about-vue.html-44f6a97e.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as s,c as a,d as l,a as e,b as i}from"./app-b649ee34.js";const c={},r=e("h1",{id:"对vue不得不吐槽的事",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#对vue不得不吐槽的事","aria-hidden":"true"},"#"),i(" 对Vue不得不吐槽的事")],-1),_=e("p",null,"为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!",-1),u=e("p",null,"总结一下,我对Vue生态不满的地方在于:",-1),d=e("ol",null,[e("li",null,"Vue总是破坏性升级,新技术完全不管旧用户的体验"),e("li",null,"你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性")],-1);function h(p,m){const t=n("BiliBili");return s(),a("div",null,[r,_,u,d,l(t,{bvid:"BV1oN411a7jp"})])}const f=o(c,[["render",h],["__file","things-I-have-to-vent-about-vue.html.vue"]]);export{f as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as s,c as a,d as l,a as e,b as i}from"./app-a9d55428.js";const c={},r=e("h1",{id:"对vue不得不吐槽的事",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#对vue不得不吐槽的事","aria-hidden":"true"},"#"),i(" 对Vue不得不吐槽的事")],-1),_=e("p",null,"为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!",-1),u=e("p",null,"总结一下,我对Vue生态不满的地方在于:",-1),d=e("ol",null,[e("li",null,"Vue总是破坏性升级,新技术完全不管旧用户的体验"),e("li",null,"你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性")],-1);function h(p,m){const t=n("BiliBili");return s(),a("div",null,[r,_,u,d,l(t,{bvid:"BV1oN411a7jp"})])}const f=o(c,[["render",h],["__file","things-I-have-to-vent-about-vue.html.vue"]]);export{f as default}; diff --git a/assets/things-I-have-to-vent-about-vue.html-38ebc534.js b/assets/things-I-have-to-vent-about-vue.html-9c4bcda2.js similarity index 91% rename from assets/things-I-have-to-vent-about-vue.html-38ebc534.js rename to assets/things-I-have-to-vent-about-vue.html-9c4bcda2.js index b06aac7b..c338e461 100644 --- a/assets/things-I-have-to-vent-about-vue.html-38ebc534.js +++ b/assets/things-I-have-to-vent-about-vue.html-9c4bcda2.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-1e305501","path":"/daily/things-I-have-to-vent-about-vue.html","title":"对Vue不得不吐槽的事","lang":"zh-CN","frontmatter":{"date":"2023-07-30T00:00:00.000Z","tag":["Frontend","Daily","Video"],"description":"对Vue不得不吐槽的事 为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了! 总结一下,我对Vue生态不满的地方在于: Vue总是破坏性升级,新技术完全不管旧用户的体验 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/things-I-have-to-vent-about-vue.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"对Vue不得不吐槽的事"}],["meta",{"property":"og:description","content":"对Vue不得不吐槽的事 为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了! 总结一下,我对Vue生态不满的地方在于: Vue总是破坏性升级,新技术完全不管旧用户的体验 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-07-30T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"对Vue不得不吐槽的事\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-07-30T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.42,"words":126},"filePathRelative":"daily/things-I-have-to-vent-about-vue.md","localizedDate":"2023年7月30日","excerpt":"

对Vue不得不吐槽的事

\\n

为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!

\\n

总结一下,我对Vue生态不满的地方在于:

\\n
    \\n
  1. Vue总是破坏性升级,新技术完全不管旧用户的体验
  2. \\n
  3. 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性
  4. \\n
\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-1e305501","path":"/daily/things-I-have-to-vent-about-vue.html","title":"对Vue不得不吐槽的事","lang":"zh-CN","frontmatter":{"date":"2023-07-30T00:00:00.000Z","tag":["Frontend","Daily","Video"],"description":"对Vue不得不吐槽的事 为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了! 总结一下,我对Vue生态不满的地方在于: Vue总是破坏性升级,新技术完全不管旧用户的体验 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/things-I-have-to-vent-about-vue.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"对Vue不得不吐槽的事"}],["meta",{"property":"og:description","content":"对Vue不得不吐槽的事 为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了! 总结一下,我对Vue生态不满的地方在于: Vue总是破坏性升级,新技术完全不管旧用户的体验 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-07-30T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"对Vue不得不吐槽的事\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-07-30T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.42,"words":126},"filePathRelative":"daily/things-I-have-to-vent-about-vue.md","localizedDate":"2023年7月30日","excerpt":"

对Vue不得不吐槽的事

\\n

为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!

\\n

总结一下,我对Vue生态不满的地方在于:

\\n
    \\n
  1. Vue总是破坏性升级,新技术完全不管旧用户的体验
  2. \\n
  3. 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性
  4. \\n
\\n","autoDesc":true}');export{e as data}; diff --git a/assets/unit-testing-overview.html-aab524e2.js b/assets/unit-testing-overview.html-3f5f616d.js similarity index 99% rename from assets/unit-testing-overview.html-aab524e2.js rename to assets/unit-testing-overview.html-3f5f616d.js index c334ff20..de4cb9aa 100644 --- a/assets/unit-testing-overview.html-aab524e2.js +++ b/assets/unit-testing-overview.html-3f5f616d.js @@ -1,4 +1,4 @@ -import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as c,c as i,a as n,b as s,d as t,e as l,f as e}from"./app-b649ee34.js";const u={},r=n("h1",{id:"单元测试概述",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#单元测试概述","aria-hidden":"true"},"#"),s(" 单元测试概述")],-1),d=n("h2",{id:"why",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#why","aria-hidden":"true"},"#"),s(" Why")],-1),k=n("p",null,"为什么要做单元测试?或者说,为什么要写测试代码?",-1),v=n("p",null,"个人总结为以下两点:",-1),m={href:"https://www.stickyminds.com/article/shift-left-approach-software-testing",target:"_blank",rel:"noopener noreferrer"},h=n("br",null,null,-1),b=n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1690532448643-e09bebb0-66f2-49f9-8686-d4a8c6b5d590.png",alt:"",loading:"lazy"},null,-1),f=n("li",null,"形成资产,方便回归测试,后续迭代重构、维护有保障",-1),g=e('

以上两点,是研发人员写测试代码的本质理由,无论什么类型的测试代码、研发人员用的什么语言、框架都适用。

What

写测试代码究竟是写什么?

个人认为测试代码主要是为了搞清楚两件事:

  1. 源码到底会不会在目标环境执行?
  2. 源码的执行结果是否符合预期?

第一件事,引出了 code coverage 代码覆盖率的概念;第二件事,则引出了 assert 断言的概念。

How

测试代码的风格

',8),_={href:"https://medium.com/@pjbgf/title-testing-code-ocd-and-the-aaa-pattern-df453975ab80",target:"_blank",rel:"noopener noreferrer"},w=e(`
  1. 组装参数
  2. 执行目标方法
  3. 执行断言
  @Test
+import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as c,c as i,a as n,b as s,d as t,e as l,f as e}from"./app-a9d55428.js";const u={},r=n("h1",{id:"单元测试概述",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#单元测试概述","aria-hidden":"true"},"#"),s(" 单元测试概述")],-1),d=n("h2",{id:"why",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#why","aria-hidden":"true"},"#"),s(" Why")],-1),k=n("p",null,"为什么要做单元测试?或者说,为什么要写测试代码?",-1),v=n("p",null,"个人总结为以下两点:",-1),m={href:"https://www.stickyminds.com/article/shift-left-approach-software-testing",target:"_blank",rel:"noopener noreferrer"},h=n("br",null,null,-1),b=n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1690532448643-e09bebb0-66f2-49f9-8686-d4a8c6b5d590.png",alt:"",loading:"lazy"},null,-1),f=n("li",null,"形成资产,方便回归测试,后续迭代重构、维护有保障",-1),g=e('

以上两点,是研发人员写测试代码的本质理由,无论什么类型的测试代码、研发人员用的什么语言、框架都适用。

What

写测试代码究竟是写什么?

个人认为测试代码主要是为了搞清楚两件事:

  1. 源码到底会不会在目标环境执行?
  2. 源码的执行结果是否符合预期?

第一件事,引出了 code coverage 代码覆盖率的概念;第二件事,则引出了 assert 断言的概念。

How

测试代码的风格

',8),_={href:"https://medium.com/@pjbgf/title-testing-code-ocd-and-the-aaa-pattern-df453975ab80",target:"_blank",rel:"noopener noreferrer"},w=e(`
  1. 组装参数
  2. 执行目标方法
  3. 执行断言
  @Test
   public void testHash() throws Exception {
     // Arrange
     String plainText = JSON.toJSONString(licenseRequest);
diff --git a/assets/unit-testing-overview.html-9597f90a.js b/assets/unit-testing-overview.html-c6b939b4.js
similarity index 89%
rename from assets/unit-testing-overview.html-9597f90a.js
rename to assets/unit-testing-overview.html-c6b939b4.js
index 6e776787..46aa3d58 100644
--- a/assets/unit-testing-overview.html-9597f90a.js
+++ b/assets/unit-testing-overview.html-c6b939b4.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-113531b4","path":"/software-testing/unit-testing-overview.html","title":"单元测试概述","lang":"zh-CN","frontmatter":{"date":"2023-07-28T00:00:00.000Z","tag":["Testing"],"description":"单元测试概述 Why 为什么要做单元测试?或者说,为什么要写测试代码? 个人总结为以下两点: 测试左移,降低修复bug的成本 形成资产,方便回归测试,后续迭代重构、维护有保障","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/unit-testing-overview.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"单元测试概述"}],["meta",{"property":"og:description","content":"单元测试概述 Why 为什么要做单元测试?或者说,为什么要写测试代码? 个人总结为以下两点: 测试左移,降低修复bug的成本 形成资产,方便回归测试,后续迭代重构、维护有保障"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2023-07-28T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"单元测试概述\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-07-28T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"Why","slug":"why","link":"#why","children":[]},{"level":2,"title":"What","slug":"what","link":"#what","children":[]},{"level":2,"title":"How","slug":"how","link":"#how","children":[{"level":3,"title":"测试代码的风格","slug":"测试代码的风格","link":"#测试代码的风格","children":[]},{"level":3,"title":"测试难点","slug":"测试难点","link":"#测试难点","children":[]},{"level":3,"title":"常用工具","slug":"常用工具","link":"#常用工具","children":[]}]},{"level":2,"title":"Bad Examples","slug":"bad-examples","link":"#bad-examples","children":[{"level":3,"title":"没有测试类","slug":"没有测试类","link":"#没有测试类","children":[]},{"level":3,"title":"没有断言","slug":"没有断言","link":"#没有断言","children":[]},{"level":3,"title":"无法重复执行","slug":"无法重复执行","link":"#无法重复执行","children":[]}]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.97,"words":892},"filePathRelative":"software-testing/unit-testing-overview.md","localizedDate":"2023年7月28日","excerpt":"

单元测试概述

\\n

Why

\\n

为什么要做单元测试?或者说,为什么要写测试代码?

\\n

个人总结为以下两点:

\\n
    \\n
  1. 测试左移,降低修复bug的成本
    \\n\\"\\"
  2. \\n
  3. 形成资产,方便回归测试,后续迭代重构、维护有保障
  4. \\n
\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-113531b4","path":"/software-testing/unit-testing-overview.html","title":"单元测试概述","lang":"zh-CN","frontmatter":{"date":"2023-07-28T00:00:00.000Z","tag":["Testing"],"description":"单元测试概述 Why 为什么要做单元测试?或者说,为什么要写测试代码? 个人总结为以下两点: 测试左移,降低修复bug的成本 形成资产,方便回归测试,后续迭代重构、维护有保障","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/unit-testing-overview.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"单元测试概述"}],["meta",{"property":"og:description","content":"单元测试概述 Why 为什么要做单元测试?或者说,为什么要写测试代码? 个人总结为以下两点: 测试左移,降低修复bug的成本 形成资产,方便回归测试,后续迭代重构、维护有保障"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2023-07-28T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"单元测试概述\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-07-28T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"Why","slug":"why","link":"#why","children":[]},{"level":2,"title":"What","slug":"what","link":"#what","children":[]},{"level":2,"title":"How","slug":"how","link":"#how","children":[{"level":3,"title":"测试代码的风格","slug":"测试代码的风格","link":"#测试代码的风格","children":[]},{"level":3,"title":"测试难点","slug":"测试难点","link":"#测试难点","children":[]},{"level":3,"title":"常用工具","slug":"常用工具","link":"#常用工具","children":[]}]},{"level":2,"title":"Bad Examples","slug":"bad-examples","link":"#bad-examples","children":[{"level":3,"title":"没有测试类","slug":"没有测试类","link":"#没有测试类","children":[]},{"level":3,"title":"没有断言","slug":"没有断言","link":"#没有断言","children":[]},{"level":3,"title":"无法重复执行","slug":"无法重复执行","link":"#无法重复执行","children":[]}]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.97,"words":892},"filePathRelative":"software-testing/unit-testing-overview.md","localizedDate":"2023年7月28日","excerpt":"

单元测试概述

\\n

Why

\\n

为什么要做单元测试?或者说,为什么要写测试代码?

\\n

个人总结为以下两点:

\\n
    \\n
  1. 测试左移,降低修复bug的成本
    \\n\\"\\"
  2. \\n
  3. 形成资产,方便回归测试,后续迭代重构、维护有保障
  4. \\n
\\n","autoDesc":true}');export{e as data}; diff --git a/assets/use-RestAssured-for-api-testing.html-b8869f75.js b/assets/use-RestAssured-for-api-testing.html-a06e24bd.js similarity index 93% rename from assets/use-RestAssured-for-api-testing.html-b8869f75.js rename to assets/use-RestAssured-for-api-testing.html-a06e24bd.js index 776c0ab9..0d878138 100644 --- a/assets/use-RestAssured-for-api-testing.html-b8869f75.js +++ b/assets/use-RestAssured-for-api-testing.html-a06e24bd.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-65b23736","path":"/software-testing/use-RestAssured-for-api-testing.html","title":"使用 RestAssured 进行 API 测试","lang":"zh-CN","frontmatter":{"date":"2023-06-09T00:00:00.000Z","tag":["Java","Testing"],"description":"使用 RestAssured 进行 API 测试 前言 本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。 本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。 What 什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。 Why 为什么要做 API 测试呢?","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-RestAssured-for-api-testing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 RestAssured 进行 API 测试"}],["meta",{"property":"og:description","content":"使用 RestAssured 进行 API 测试 前言 本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。 本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。 What 什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。 Why 为什么要做 API 测试呢?"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2023-06-09T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 RestAssured 进行 API 测试\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-06-09T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"What","slug":"what","link":"#what","children":[]},{"level":2,"title":"Why","slug":"why","link":"#why","children":[]},{"level":2,"title":"为什么不用Postman","slug":"为什么不用postman","link":"#为什么不用postman","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"快速上手","slug":"快速上手","link":"#快速上手","children":[]},{"level":2,"title":"通用设置","slug":"通用设置","link":"#通用设置","children":[]},{"level":2,"title":"请求示例","slug":"请求示例","link":"#请求示例","children":[]},{"level":2,"title":"接口依赖","slug":"接口依赖","link":"#接口依赖","children":[]},{"level":2,"title":"上传示例","slug":"上传示例","link":"#上传示例","children":[]},{"level":2,"title":"下载示例","slug":"下载示例","link":"#下载示例","children":[]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[]},{"level":2,"title":"其他问题","slug":"其他问题","link":"#其他问题","children":[{"level":3,"title":"为什么不用 Pytest","slug":"为什么不用-pytest","link":"#为什么不用-pytest","children":[]},{"level":3,"title":"这也是单元测试吗","slug":"这也是单元测试吗","link":"#这也是单元测试吗","children":[]}]},{"level":2,"title":"参考资料","slug":"参考资料","link":"#参考资料","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":6.99,"words":2096},"filePathRelative":"software-testing/use-RestAssured-for-api-testing.md","localizedDate":"2023年6月9日","excerpt":"

使用 RestAssured 进行 API 测试

\\n

前言

\\n

本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。

\\n

本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。

\\n

What

\\n

什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。

\\n

Why

\\n

为什么要做 API 测试呢?

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-65b23736","path":"/software-testing/use-RestAssured-for-api-testing.html","title":"使用 RestAssured 进行 API 测试","lang":"zh-CN","frontmatter":{"date":"2023-06-09T00:00:00.000Z","tag":["Java","Testing"],"description":"使用 RestAssured 进行 API 测试 前言 本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。 本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。 What 什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。 Why 为什么要做 API 测试呢?","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-RestAssured-for-api-testing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 RestAssured 进行 API 测试"}],["meta",{"property":"og:description","content":"使用 RestAssured 进行 API 测试 前言 本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。 本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。 What 什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。 Why 为什么要做 API 测试呢?"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2023-06-09T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 RestAssured 进行 API 测试\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-06-09T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"What","slug":"what","link":"#what","children":[]},{"level":2,"title":"Why","slug":"why","link":"#why","children":[]},{"level":2,"title":"为什么不用Postman","slug":"为什么不用postman","link":"#为什么不用postman","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"快速上手","slug":"快速上手","link":"#快速上手","children":[]},{"level":2,"title":"通用设置","slug":"通用设置","link":"#通用设置","children":[]},{"level":2,"title":"请求示例","slug":"请求示例","link":"#请求示例","children":[]},{"level":2,"title":"接口依赖","slug":"接口依赖","link":"#接口依赖","children":[]},{"level":2,"title":"上传示例","slug":"上传示例","link":"#上传示例","children":[]},{"level":2,"title":"下载示例","slug":"下载示例","link":"#下载示例","children":[]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[]},{"level":2,"title":"其他问题","slug":"其他问题","link":"#其他问题","children":[{"level":3,"title":"为什么不用 Pytest","slug":"为什么不用-pytest","link":"#为什么不用-pytest","children":[]},{"level":3,"title":"这也是单元测试吗","slug":"这也是单元测试吗","link":"#这也是单元测试吗","children":[]}]},{"level":2,"title":"参考资料","slug":"参考资料","link":"#参考资料","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":6.99,"words":2096},"filePathRelative":"software-testing/use-RestAssured-for-api-testing.md","localizedDate":"2023年6月9日","excerpt":"

使用 RestAssured 进行 API 测试

\\n

前言

\\n

本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。

\\n

本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。

\\n

What

\\n

什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。

\\n

Why

\\n

为什么要做 API 测试呢?

","autoDesc":true}');export{e as data}; diff --git a/assets/use-RestAssured-for-api-testing.html-badc2513.js b/assets/use-RestAssured-for-api-testing.html-db3b5b16.js similarity index 99% rename from assets/use-RestAssured-for-api-testing.html-badc2513.js rename to assets/use-RestAssured-for-api-testing.html-db3b5b16.js index 046c0b55..bc67b620 100644 --- a/assets/use-RestAssured-for-api-testing.html-badc2513.js +++ b/assets/use-RestAssured-for-api-testing.html-db3b5b16.js @@ -1,4 +1,4 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as e,o as c,c as u,a as s,b as n,d as t,f as p}from"./app-b649ee34.js";const l={},i=p('

使用 RestAssured 进行 API 测试

前言

本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。

本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。

What

什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。

Why

为什么要做 API 测试呢?

考虑有过这样的场景:

  • 加一个新功能,自测没问题,结果被测试人员发现一个旧模块出了问题,感到措手不及
  • 后端写好了接口,前端还没开发好界面,于是感觉不方便自测,因为没有界面,只好催前端快去做页面

API 测试就是来解决上述问题的。做 API 测试的原因有:

  • 必要性:做回归测试,避免添加新功能时破坏旧功能。
  • 便利性:方便本地调试,不用部署到线上,依赖界面去测试。
  • 资产化:让测试用例变成资产,与团队共享。

当然,要做好 API 测试,还要接受这样的认知: 接口自动化测试并不仅仅是测试人员事情,研发人员也有责任把它做好。 否则,研发人员难免会觉得这不关我的事, 从而不愿意写这种代码。 建议研发人员从以下方便思考其好处,提升行动的积极性:

  • 减少阻塞,接口自测不再依赖前端
  • 提高效率,本地就能自测,不用把应用部署到线上环境
  • 提高质量,减少部署到研发环境、前端一调用接口就 500 的情况

为什么不用Postman

Postman 确实是符合直觉的接口调试的第一选项。 但注意,调试不等于测试。

Postman 在实践过程中,最大的问题在于,无法将测试用例有效地资产化:

  • 你会在 Postman 里写断言吗?很少吧,你其实是在用肉眼去检查接口成功与否,这本质还是手工测试
  • 你的 Postman 数据能与团队共享吗?不能吧,大多数人的 Postman 数据是在本地的,也不会去付费创建一个团队以共享数据
  • 你的 Postman 数据在有版本管理吗?没有吧,大多数人的 Postman 数据是与源代码分离的,不利于维护与管理
',18),k={href:"https://github.com/postmanlabs/newman",target:"_blank",rel:"noopener noreferrer"},r=p(`

考虑源代码是 Java,使用 RestAssured,编写 API 测试代码用同一种语言,可以减少使用者的心智负担较轻;并且与源代码放在同一个 Git 仓库中,易于管理。

因此,我仍然会使用 Postman,但更多是把它应用在出现线上问题时,直接复制一个 cURL 用来复现、排查问题的情况。

安装

下面将介绍如何用 Maven 安装 RestAssured。

复制以下内容到 pom.xml 即可。

    <!-- RestAssured for api testing -->
+import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as e,o as c,c as u,a as s,b as n,d as t,f as p}from"./app-a9d55428.js";const l={},i=p('

使用 RestAssured 进行 API 测试

前言

本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。

本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。

What

什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。

Why

为什么要做 API 测试呢?

考虑有过这样的场景:

  • 加一个新功能,自测没问题,结果被测试人员发现一个旧模块出了问题,感到措手不及
  • 后端写好了接口,前端还没开发好界面,于是感觉不方便自测,因为没有界面,只好催前端快去做页面

API 测试就是来解决上述问题的。做 API 测试的原因有:

  • 必要性:做回归测试,避免添加新功能时破坏旧功能。
  • 便利性:方便本地调试,不用部署到线上,依赖界面去测试。
  • 资产化:让测试用例变成资产,与团队共享。

当然,要做好 API 测试,还要接受这样的认知: 接口自动化测试并不仅仅是测试人员事情,研发人员也有责任把它做好。 否则,研发人员难免会觉得这不关我的事, 从而不愿意写这种代码。 建议研发人员从以下方便思考其好处,提升行动的积极性:

  • 减少阻塞,接口自测不再依赖前端
  • 提高效率,本地就能自测,不用把应用部署到线上环境
  • 提高质量,减少部署到研发环境、前端一调用接口就 500 的情况

为什么不用Postman

Postman 确实是符合直觉的接口调试的第一选项。 但注意,调试不等于测试。

Postman 在实践过程中,最大的问题在于,无法将测试用例有效地资产化:

  • 你会在 Postman 里写断言吗?很少吧,你其实是在用肉眼去检查接口成功与否,这本质还是手工测试
  • 你的 Postman 数据能与团队共享吗?不能吧,大多数人的 Postman 数据是在本地的,也不会去付费创建一个团队以共享数据
  • 你的 Postman 数据在有版本管理吗?没有吧,大多数人的 Postman 数据是与源代码分离的,不利于维护与管理
',18),k={href:"https://github.com/postmanlabs/newman",target:"_blank",rel:"noopener noreferrer"},r=p(`

考虑源代码是 Java,使用 RestAssured,编写 API 测试代码用同一种语言,可以减少使用者的心智负担较轻;并且与源代码放在同一个 Git 仓库中,易于管理。

因此,我仍然会使用 Postman,但更多是把它应用在出现线上问题时,直接复制一个 cURL 用来复现、排查问题的情况。

安装

下面将介绍如何用 Maven 安装 RestAssured。

复制以下内容到 pom.xml 即可。

    <!-- RestAssured for api testing -->
     <dependency>
         <groupId>io.rest-assured</groupId>
         <artifactId>rest-assured</artifactId>
diff --git a/assets/use-claude2-instead-of-chatgpt.html-cc6aebc1.js b/assets/use-claude2-instead-of-chatgpt.html-c55c251a.js
similarity index 99%
rename from assets/use-claude2-instead-of-chatgpt.html-cc6aebc1.js
rename to assets/use-claude2-instead-of-chatgpt.html-c55c251a.js
index 949b6f63..626f2a53 100644
--- a/assets/use-claude2-instead-of-chatgpt.html-cc6aebc1.js
+++ b/assets/use-claude2-instead-of-chatgpt.html-c55c251a.js
@@ -1 +1 @@
-import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as c,c as r,e as d,d as t,a as e,b as a,f as m}from"./app-b649ee34.js";const g={},s=e("h1",{id:"再见chatgpt-我选择claude2",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#再见chatgpt-我选择claude2","aria-hidden":"true"},"#"),a(" 再见ChatGPT,我选择Claude2!")],-1),h=e("p",null,"大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。",-1),b=e("p",null,"首先要评测的当然是ChatGPT了,因为最早用的就是它。",-1),p=e("figure",null,[e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1691497563137-33a17053-c9a2-47bf-bc2e-25f4d567d406.jpeg",alt:"",width:"200",tabindex:"0",loading:"lazy"}),e("figcaption")],-1),u=m('

现在来看呢,没有当初那么惊艳了,因为已经有很多的平替了,甚至是超替了。当然现在我还会用它的唯一原因呢,就是这个Voice Control插件。

它不但可以让我进行语音输入,还可以让ChatGPT变成语音输出,那这一点很方便我进行英语口语练习。

当然要说口语练习的话,可以用手机App Call Annie。

不过呢,因为它要求iOS 16,我不想系统升级,所以没有用。另外呢,想练习的话,其实还是要去不断的对自己的词语、句子去做一个调整、优化的,那这一点我还是认为ChatGPT带有文字的输出会更适合我,方便我做笔记。

ChatGPT 注册还是有门槛的。因为大家知道虽然它可以免费使用,但实际上要注册成功的话,要么是买一个账号,要么是买短信。实际上还是付费了。当初的话我是花了16块钱买了个短信,然后注册账号的,现在有很多其他的免费替代方案,所以现在是不推荐注册 ChatGPT 的,毕竟白嫖更香嘛。

那下一个评测对象是 New Bing。New Bing 刚出来时很惊艳,差点成为大家的AI女友。后来微软做了很多调整,现在已经被束缚住了手脚。

当然,从用户界面上来说 New Bing 是有着最佳交互体验的。现在可以上传图片,支持在线链接,也支持粘贴上传,体验感极佳。

然后呢,对于New Bing呢,每回答一个问题,后面会有三个提示的快捷问题选项,可以让我们更方便地去问相关的问题,这也是一个很好的体验。

但实际效果来看,对话有长度限制,当然这是小问题,30次回答已经不少了。

但最蛋疼的是它的自我审察,面对敏感问题,它会避而不答,甚至终止对话。



那这样的话就体验很差了,因此呢,我的评价是绣花枕头,中看不中用。

要想使用 New Bing呢,之前是要去加入 wait list,等待微软的审批。现在据说是放开了,应该是可以更容易注册了。但是考虑到前面描述的不尽如人意的体验,我是认为没有必要去注册了,因为还有更好的。

那么接下来我们要评测的就是ChatGPT的劲敌,原Open AI成员打造的Claude。

Claude 2 有着超长的token,100K。


并且与其他AI相比呢,它有一个特点是比较的健谈。也就是说回答了你一个问题后,他会说,In more detail,接着用更丰富的语言去回答一遍,这其实是一个很好的特性。

',9),_={href:"https://poe.com/login",target:"_blank",rel:"noopener noreferrer"},f=e("br",null,null,-1),y=e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1691497388132-d255ee3f-f1e8-4ae7-8870-5cf51d9906a9.png",alt:"",loading:"lazy"},null,-1),w=e("br",null,null,-1),k={href:"https://poe.com/login",target:"_blank",rel:"noopener noreferrer"},v=e("br",null,null,-1),z=e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1691141726927-7d6cb97a-8e17-4687-abe1-22e46ed3945a.png",alt:"",loading:"lazy"},null,-1),C=e("p",null,[a("最后,来看看面对恶意提问,Claude2是如何回答的:"),e("br"),e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1691499213415-94b6c6b0-b59b-43b9-96b5-b346a2ee7e1f.png",alt:"",loading:"lazy"})],-1);function B(I,N){const l=n("BiliBili"),i=n("ExternalLinkIcon");return c(),r("div",null,[s,h,b,d(" more "),p,t(l,{bvid:"BV1yj411z7zr"}),u,e("p",null,[a("那么有没有一款软件,它拥有上述AI的所有的优点呢?有啊,那就是"),e("a",_,[a("poe.com"),t(i)]),a("。对于还没有使用AI或者还在使用国产AI的同学呢,我非常推荐使用它,用谷歌账号即可注册使用。"),f,y,w,a(" 这里面有各种的AI任你挑选。同时呢,他也集成了 New Bing 的快捷回复的选项。所以说,我认为当前最佳的组合就是"),e("a",k,[a("poe.com"),t(i)]),a(" + claude2,这将是一个体验极佳的使用方案。"),v,z]),C])}const T=o(g,[["render",B],["__file","use-claude2-instead-of-chatgpt.html.vue"]]);export{T as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as c,c as r,e as d,d as t,a as e,b as a,f as m}from"./app-a9d55428.js";const g={},s=e("h1",{id:"再见chatgpt-我选择claude2",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#再见chatgpt-我选择claude2","aria-hidden":"true"},"#"),a(" 再见ChatGPT,我选择Claude2!")],-1),h=e("p",null,"大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。",-1),b=e("p",null,"首先要评测的当然是ChatGPT了,因为最早用的就是它。",-1),p=e("figure",null,[e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1691497563137-33a17053-c9a2-47bf-bc2e-25f4d567d406.jpeg",alt:"",width:"200",tabindex:"0",loading:"lazy"}),e("figcaption")],-1),u=m('

现在来看呢,没有当初那么惊艳了,因为已经有很多的平替了,甚至是超替了。当然现在我还会用它的唯一原因呢,就是这个Voice Control插件。

它不但可以让我进行语音输入,还可以让ChatGPT变成语音输出,那这一点很方便我进行英语口语练习。

当然要说口语练习的话,可以用手机App Call Annie。

不过呢,因为它要求iOS 16,我不想系统升级,所以没有用。另外呢,想练习的话,其实还是要去不断的对自己的词语、句子去做一个调整、优化的,那这一点我还是认为ChatGPT带有文字的输出会更适合我,方便我做笔记。

ChatGPT 注册还是有门槛的。因为大家知道虽然它可以免费使用,但实际上要注册成功的话,要么是买一个账号,要么是买短信。实际上还是付费了。当初的话我是花了16块钱买了个短信,然后注册账号的,现在有很多其他的免费替代方案,所以现在是不推荐注册 ChatGPT 的,毕竟白嫖更香嘛。

那下一个评测对象是 New Bing。New Bing 刚出来时很惊艳,差点成为大家的AI女友。后来微软做了很多调整,现在已经被束缚住了手脚。

当然,从用户界面上来说 New Bing 是有着最佳交互体验的。现在可以上传图片,支持在线链接,也支持粘贴上传,体验感极佳。

然后呢,对于New Bing呢,每回答一个问题,后面会有三个提示的快捷问题选项,可以让我们更方便地去问相关的问题,这也是一个很好的体验。

但实际效果来看,对话有长度限制,当然这是小问题,30次回答已经不少了。

但最蛋疼的是它的自我审察,面对敏感问题,它会避而不答,甚至终止对话。



那这样的话就体验很差了,因此呢,我的评价是绣花枕头,中看不中用。

要想使用 New Bing呢,之前是要去加入 wait list,等待微软的审批。现在据说是放开了,应该是可以更容易注册了。但是考虑到前面描述的不尽如人意的体验,我是认为没有必要去注册了,因为还有更好的。

那么接下来我们要评测的就是ChatGPT的劲敌,原Open AI成员打造的Claude。

Claude 2 有着超长的token,100K。


并且与其他AI相比呢,它有一个特点是比较的健谈。也就是说回答了你一个问题后,他会说,In more detail,接着用更丰富的语言去回答一遍,这其实是一个很好的特性。

',9),_={href:"https://poe.com/login",target:"_blank",rel:"noopener noreferrer"},f=e("br",null,null,-1),y=e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1691497388132-d255ee3f-f1e8-4ae7-8870-5cf51d9906a9.png",alt:"",loading:"lazy"},null,-1),w=e("br",null,null,-1),k={href:"https://poe.com/login",target:"_blank",rel:"noopener noreferrer"},v=e("br",null,null,-1),z=e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1691141726927-7d6cb97a-8e17-4687-abe1-22e46ed3945a.png",alt:"",loading:"lazy"},null,-1),C=e("p",null,[a("最后,来看看面对恶意提问,Claude2是如何回答的:"),e("br"),e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1691499213415-94b6c6b0-b59b-43b9-96b5-b346a2ee7e1f.png",alt:"",loading:"lazy"})],-1);function B(I,N){const l=n("BiliBili"),i=n("ExternalLinkIcon");return c(),r("div",null,[s,h,b,d(" more "),p,t(l,{bvid:"BV1yj411z7zr"}),u,e("p",null,[a("那么有没有一款软件,它拥有上述AI的所有的优点呢?有啊,那就是"),e("a",_,[a("poe.com"),t(i)]),a("。对于还没有使用AI或者还在使用国产AI的同学呢,我非常推荐使用它,用谷歌账号即可注册使用。"),f,y,w,a(" 这里面有各种的AI任你挑选。同时呢,他也集成了 New Bing 的快捷回复的选项。所以说,我认为当前最佳的组合就是"),e("a",k,[a("poe.com"),t(i)]),a(" + claude2,这将是一个体验极佳的使用方案。"),v,z]),C])}const T=o(g,[["render",B],["__file","use-claude2-instead-of-chatgpt.html.vue"]]);export{T as default}; diff --git a/assets/use-claude2-instead-of-chatgpt.html-a143e23e.js b/assets/use-claude2-instead-of-chatgpt.html-d4f72795.js similarity index 84% rename from assets/use-claude2-instead-of-chatgpt.html-a143e23e.js rename to assets/use-claude2-instead-of-chatgpt.html-d4f72795.js index 032ed8a4..f5b152a3 100644 --- a/assets/use-claude2-instead-of-chatgpt.html-a143e23e.js +++ b/assets/use-claude2-instead-of-chatgpt.html-d4f72795.js @@ -1 +1 @@ -const t=JSON.parse('{"key":"v-404740fa","path":"/daily/use-claude2-instead-of-chatgpt.html","title":"再见ChatGPT,我选择Claude2!","lang":"zh-CN","frontmatter":{"date":"2023-08-08T00:00:00.000Z","tag":["Video","AI","Tool"],"description":"再见ChatGPT,我选择Claude2! 大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。 首先要评测的当然是ChatGPT了,因为最早用的就是它。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/use-claude2-instead-of-chatgpt.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"再见ChatGPT,我选择Claude2!"}],["meta",{"property":"og:description","content":"再见ChatGPT,我选择Claude2! 大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。 首先要评测的当然是ChatGPT了,因为最早用的就是它。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:tag","content":"AI"}],["meta",{"property":"article:tag","content":"Tool"}],["meta",{"property":"article:published_time","content":"2023-08-08T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"再见ChatGPT,我选择Claude2!\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-08T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.76,"words":1128},"filePathRelative":"daily/use-claude2-instead-of-chatgpt.md","localizedDate":"2023年8月8日","excerpt":"

再见ChatGPT,我选择Claude2!

\\n

大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。

\\n

首先要评测的当然是ChatGPT了,因为最早用的就是它。

\\n","autoDesc":true}');export{t as data}; +const t=JSON.parse('{"key":"v-404740fa","path":"/daily/use-claude2-instead-of-chatgpt.html","title":"再见ChatGPT,我选择Claude2!","lang":"zh-CN","frontmatter":{"date":"2023-08-08T00:00:00.000Z","tag":["Video","AI","Tool"],"description":"再见ChatGPT,我选择Claude2! 大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。 首先要评测的当然是ChatGPT了,因为最早用的就是它。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/use-claude2-instead-of-chatgpt.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"再见ChatGPT,我选择Claude2!"}],["meta",{"property":"og:description","content":"再见ChatGPT,我选择Claude2! 大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。 首先要评测的当然是ChatGPT了,因为最早用的就是它。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:tag","content":"AI"}],["meta",{"property":"article:tag","content":"Tool"}],["meta",{"property":"article:published_time","content":"2023-08-08T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"再见ChatGPT,我选择Claude2!\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-08T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.76,"words":1128},"filePathRelative":"daily/use-claude2-instead-of-chatgpt.md","localizedDate":"2023年8月8日","excerpt":"

再见ChatGPT,我选择Claude2!

\\n

大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。

\\n

首先要评测的当然是ChatGPT了,因为最早用的就是它。

\\n","autoDesc":true}');export{t as data}; diff --git a/assets/use-command-line-tool-to-manage-gitlab-merge-request.html-4c25576d.js b/assets/use-command-line-tool-to-manage-gitlab-merge-request.html-1f237536.js similarity index 95% rename from assets/use-command-line-tool-to-manage-gitlab-merge-request.html-4c25576d.js rename to assets/use-command-line-tool-to-manage-gitlab-merge-request.html-1f237536.js index 2c100699..19ea6a02 100644 --- a/assets/use-command-line-tool-to-manage-gitlab-merge-request.html-4c25576d.js +++ b/assets/use-command-line-tool-to-manage-gitlab-merge-request.html-1f237536.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-071be141","path":"/git/use-command-line-tool-to-manage-gitlab-merge-request.html","title":"操作 Gitlab MR 的命令行工具","lang":"zh-CN","frontmatter":{"date":"2023-03-23T00:00:00.000Z","tag":["Git","GitLab","Python"],"description":"操作 Gitlab MR 的命令行工具 背景 为什么开发这个工具?主要解决以下问题: 提测、上 UAT 时,避免漏合代码。 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?” 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。 可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。 并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。 对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/use-command-line-tool-to-manage-gitlab-merge-request.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"操作 Gitlab MR 的命令行工具"}],["meta",{"property":"og:description","content":"操作 Gitlab MR 的命令行工具 背景 为什么开发这个工具?主要解决以下问题: 提测、上 UAT 时,避免漏合代码。 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?” 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。 可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。 并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。 对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:tag","content":"GitLab"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:published_time","content":"2023-03-23T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"操作 Gitlab MR 的命令行工具\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-03-23T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[{"level":3,"title":"解压zip","slug":"解压zip","link":"#解压zip","children":[]},{"level":3,"title":"安装git bash","slug":"安装git-bash","link":"#安装git-bash","children":[]}]},{"level":2,"title":"配置","slug":"配置","link":"#配置","children":[{"level":3,"title":"gitlab_token","slug":"gitlab-token","link":"#gitlab-token","children":[]},{"level":3,"title":"codebases","slug":"codebases","link":"#codebases","children":[]},{"level":3,"title":"环境变量","slug":"环境变量","link":"#环境变量","children":[]},{"level":3,"title":"IDEA","slug":"idea","link":"#idea","children":[]}]},{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[{"level":3,"title":"创建MR","slug":"创建mr","link":"#创建mr","children":[]},{"level":3,"title":"查看MR","slug":"查看mr","link":"#查看mr","children":[]},{"level":3,"title":"合并MR","slug":"合并mr","link":"#合并mr","children":[]},{"level":3,"title":"冲突处理","slug":"冲突处理","link":"#冲突处理","children":[]}]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.42,"words":1325},"filePathRelative":"git/use-command-line-tool-to-manage-gitlab-merge-request.md","localizedDate":"2023年3月23日","excerpt":"

操作 Gitlab MR 的命令行工具

\\n

背景

\\n

为什么开发这个工具?主要解决以下问题:

\\n
    \\n
  1. 提测、上 UAT 时,避免漏合代码。
  2. \\n
  3. 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?”
  4. \\n
  5. 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。
  6. \\n
\\n

可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。
\\n并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。
\\n对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-071be141","path":"/git/use-command-line-tool-to-manage-gitlab-merge-request.html","title":"操作 Gitlab MR 的命令行工具","lang":"zh-CN","frontmatter":{"date":"2023-03-23T00:00:00.000Z","tag":["Git","GitLab","Python"],"description":"操作 Gitlab MR 的命令行工具 背景 为什么开发这个工具?主要解决以下问题: 提测、上 UAT 时,避免漏合代码。 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?” 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。 可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。 并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。 对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/use-command-line-tool-to-manage-gitlab-merge-request.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"操作 Gitlab MR 的命令行工具"}],["meta",{"property":"og:description","content":"操作 Gitlab MR 的命令行工具 背景 为什么开发这个工具?主要解决以下问题: 提测、上 UAT 时,避免漏合代码。 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?” 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。 可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。 并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。 对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:tag","content":"GitLab"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:published_time","content":"2023-03-23T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"操作 Gitlab MR 的命令行工具\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-03-23T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[{"level":3,"title":"解压zip","slug":"解压zip","link":"#解压zip","children":[]},{"level":3,"title":"安装git bash","slug":"安装git-bash","link":"#安装git-bash","children":[]}]},{"level":2,"title":"配置","slug":"配置","link":"#配置","children":[{"level":3,"title":"gitlab_token","slug":"gitlab-token","link":"#gitlab-token","children":[]},{"level":3,"title":"codebases","slug":"codebases","link":"#codebases","children":[]},{"level":3,"title":"环境变量","slug":"环境变量","link":"#环境变量","children":[]},{"level":3,"title":"IDEA","slug":"idea","link":"#idea","children":[]}]},{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[{"level":3,"title":"创建MR","slug":"创建mr","link":"#创建mr","children":[]},{"level":3,"title":"查看MR","slug":"查看mr","link":"#查看mr","children":[]},{"level":3,"title":"合并MR","slug":"合并mr","link":"#合并mr","children":[]},{"level":3,"title":"冲突处理","slug":"冲突处理","link":"#冲突处理","children":[]}]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.42,"words":1325},"filePathRelative":"git/use-command-line-tool-to-manage-gitlab-merge-request.md","localizedDate":"2023年3月23日","excerpt":"

操作 Gitlab MR 的命令行工具

\\n

背景

\\n

为什么开发这个工具?主要解决以下问题:

\\n
    \\n
  1. 提测、上 UAT 时,避免漏合代码。
  2. \\n
  3. 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?”
  4. \\n
  5. 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。
  6. \\n
\\n

可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。
\\n并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。
\\n对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。

","autoDesc":true}');export{e as data}; diff --git a/assets/use-command-line-tool-to-manage-gitlab-merge-request.html-bb0e24b0.js b/assets/use-command-line-tool-to-manage-gitlab-merge-request.html-e01c2260.js similarity index 99% rename from assets/use-command-line-tool-to-manage-gitlab-merge-request.html-bb0e24b0.js rename to assets/use-command-line-tool-to-manage-gitlab-merge-request.html-e01c2260.js index fd84a75e..0ce2cb29 100644 --- a/assets/use-command-line-tool-to-manage-gitlab-merge-request.html-bb0e24b0.js +++ b/assets/use-command-line-tool-to-manage-gitlab-merge-request.html-e01c2260.js @@ -1,4 +1,4 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as r,c as l,a,b as e,d as s,f as i}from"./app-b649ee34.js";const d={},c=i('

操作 Gitlab MR 的命令行工具

背景

为什么开发这个工具?主要解决以下问题:

  1. 提测、上 UAT 时,避免漏合代码。
  2. 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?”
  3. 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。

可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。
并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。
对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。

安装

解压zip

下载并解压文件:

',8),p={href:"https://r0e715v8ejr.feishu.cn/file/IxH4bYAOkowK08xSid1crXcSnRo",target:"_blank",rel:"noopener noreferrer"},g={href:"https://r0e715v8ejr.feishu.cn/file/ORa3buA3donF3TxxPVwcHSYnnQb",target:"_blank",rel:"noopener noreferrer"},m=a("h3",{id:"安装git-bash",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#安装git-bash","aria-hidden":"true"},"#"),e(" 安装git bash")],-1),u=a("p",null,[e("Windows系统才要安装。"),a("br"),e(" 如果 git bash 版本不足 2.41.0,最好安装最新版本。")],-1),h={href:"https://gitforwindows.org/",target:"_blank",rel:"noopener noreferrer"},b=i(`

配置

新增文件

vi ~/.mr-config.json
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as r,c as l,a,b as e,d as s,f as i}from"./app-a9d55428.js";const d={},c=i('

操作 Gitlab MR 的命令行工具

背景

为什么开发这个工具?主要解决以下问题:

  1. 提测、上 UAT 时,避免漏合代码。
  2. 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?”
  3. 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。

可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。
并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。
对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。

安装

解压zip

下载并解压文件:

',8),p={href:"https://r0e715v8ejr.feishu.cn/file/IxH4bYAOkowK08xSid1crXcSnRo",target:"_blank",rel:"noopener noreferrer"},g={href:"https://r0e715v8ejr.feishu.cn/file/ORa3buA3donF3TxxPVwcHSYnnQb",target:"_blank",rel:"noopener noreferrer"},m=a("h3",{id:"安装git-bash",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#安装git-bash","aria-hidden":"true"},"#"),e(" 安装git bash")],-1),u=a("p",null,[e("Windows系统才要安装。"),a("br"),e(" 如果 git bash 版本不足 2.41.0,最好安装最新版本。")],-1),h={href:"https://gitforwindows.org/",target:"_blank",rel:"noopener noreferrer"},b=i(`

配置

新增文件

vi ~/.mr-config.json
 

复制以下内容:

{
   "gitlab_url":"https://your-gitlab.com",
   "gitlab_token":"your-token",
diff --git a/assets/use-cypress-for-e2e-testing.html-a3aed518.js b/assets/use-cypress-for-e2e-testing.html-2b65285a.js
similarity index 93%
rename from assets/use-cypress-for-e2e-testing.html-a3aed518.js
rename to assets/use-cypress-for-e2e-testing.html-2b65285a.js
index b4ef71b7..dd3211df 100644
--- a/assets/use-cypress-for-e2e-testing.html-a3aed518.js
+++ b/assets/use-cypress-for-e2e-testing.html-2b65285a.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-c488ac58","path":"/software-testing/use-cypress-for-e2e-testing.html","title":"使用 Cypress 进行端对端测试","lang":"zh-CN","frontmatter":{"date":"2020-12-08T00:00:00.000Z","tag":["Node.js","Testing"],"description":"使用 Cypress 进行端对端测试 为什么写端对端测试 写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。 谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。 为什么用 Cypress 文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypress","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-cypress-for-e2e-testing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 Cypress 进行端对端测试"}],["meta",{"property":"og:description","content":"使用 Cypress 进行端对端测试 为什么写端对端测试 写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。 谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。 为什么用 Cypress 文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypress"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2020-12-08T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 Cypress 进行端对端测试\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2020-12-08T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"为什么写端对端测试","slug":"为什么写端对端测试","link":"#为什么写端对端测试","children":[]},{"level":2,"title":"为什么用 Cypress","slug":"为什么用-cypress","link":"#为什么用-cypress","children":[]},{"level":2,"title":"快速开始","slug":"快速开始","link":"#快速开始","children":[{"level":3,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":3,"title":"加速下载","slug":"加速下载","link":"#加速下载","children":[]},{"level":3,"title":"目录结构","slug":"目录结构","link":"#目录结构","children":[]},{"level":3,"title":"与 Jest 协同工作","slug":"与-jest-协同工作","link":"#与-jest-协同工作","children":[]},{"level":3,"title":"检查依赖及生产安装依赖命令","slug":"检查依赖及生产安装依赖命令","link":"#检查依赖及生产安装依赖命令","children":[]},{"level":3,"title":"第一个用例","slug":"第一个用例","link":"#第一个用例","children":[]},{"level":3,"title":"更复杂的示例","slug":"更复杂的示例","link":"#更复杂的示例","children":[]}]},{"level":2,"title":"结合TypeScript","slug":"结合typescript","link":"#结合typescript","children":[]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[{"level":3,"title":"直接运行 Cypress","slug":"直接运行-cypress","link":"#直接运行-cypress","children":[]},{"level":3,"title":"使用 start-server-and-test","slug":"使用-start-server-and-test","link":"#使用-start-server-and-test","children":[]},{"level":3,"title":"This job is stuck","slug":"this-job-is-stuck","link":"#this-job-is-stuck","children":[]},{"level":3,"title":"Cypress Dashbord","slug":"cypress-dashbord","link":"#cypress-dashbord","children":[]}]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]},{"level":2,"title":"拓展阅读","slug":"拓展阅读","link":"#拓展阅读","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":8.04,"words":2413},"filePathRelative":"software-testing/use-cypress-for-e2e-testing.md","localizedDate":"2020年12月8日","excerpt":"

使用 Cypress 进行端对端测试

\\n

为什么写端对端测试

\\n

写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。

\\n

谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。

\\n

为什么用 Cypress

\\n

文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypress

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-c488ac58","path":"/software-testing/use-cypress-for-e2e-testing.html","title":"使用 Cypress 进行端对端测试","lang":"zh-CN","frontmatter":{"date":"2020-12-08T00:00:00.000Z","tag":["Node.js","Testing"],"description":"使用 Cypress 进行端对端测试 为什么写端对端测试 写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。 谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。 为什么用 Cypress 文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypress","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-cypress-for-e2e-testing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 Cypress 进行端对端测试"}],["meta",{"property":"og:description","content":"使用 Cypress 进行端对端测试 为什么写端对端测试 写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。 谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。 为什么用 Cypress 文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypress"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2020-12-08T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 Cypress 进行端对端测试\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2020-12-08T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"为什么写端对端测试","slug":"为什么写端对端测试","link":"#为什么写端对端测试","children":[]},{"level":2,"title":"为什么用 Cypress","slug":"为什么用-cypress","link":"#为什么用-cypress","children":[]},{"level":2,"title":"快速开始","slug":"快速开始","link":"#快速开始","children":[{"level":3,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":3,"title":"加速下载","slug":"加速下载","link":"#加速下载","children":[]},{"level":3,"title":"目录结构","slug":"目录结构","link":"#目录结构","children":[]},{"level":3,"title":"与 Jest 协同工作","slug":"与-jest-协同工作","link":"#与-jest-协同工作","children":[]},{"level":3,"title":"检查依赖及生产安装依赖命令","slug":"检查依赖及生产安装依赖命令","link":"#检查依赖及生产安装依赖命令","children":[]},{"level":3,"title":"第一个用例","slug":"第一个用例","link":"#第一个用例","children":[]},{"level":3,"title":"更复杂的示例","slug":"更复杂的示例","link":"#更复杂的示例","children":[]}]},{"level":2,"title":"结合TypeScript","slug":"结合typescript","link":"#结合typescript","children":[]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[{"level":3,"title":"直接运行 Cypress","slug":"直接运行-cypress","link":"#直接运行-cypress","children":[]},{"level":3,"title":"使用 start-server-and-test","slug":"使用-start-server-and-test","link":"#使用-start-server-and-test","children":[]},{"level":3,"title":"This job is stuck","slug":"this-job-is-stuck","link":"#this-job-is-stuck","children":[]},{"level":3,"title":"Cypress Dashbord","slug":"cypress-dashbord","link":"#cypress-dashbord","children":[]}]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]},{"level":2,"title":"拓展阅读","slug":"拓展阅读","link":"#拓展阅读","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":8.04,"words":2413},"filePathRelative":"software-testing/use-cypress-for-e2e-testing.md","localizedDate":"2020年12月8日","excerpt":"

使用 Cypress 进行端对端测试

\\n

为什么写端对端测试

\\n

写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。

\\n

谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。

\\n

为什么用 Cypress

\\n

文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypress

","autoDesc":true}');export{e as data}; diff --git a/assets/use-cypress-for-e2e-testing.html-1c183bce.js b/assets/use-cypress-for-e2e-testing.html-450ffdcd.js similarity index 99% rename from assets/use-cypress-for-e2e-testing.html-1c183bce.js rename to assets/use-cypress-for-e2e-testing.html-450ffdcd.js index 4f5f1e48..d4126cd0 100644 --- a/assets/use-cypress-for-e2e-testing.html-1c183bce.js +++ b/assets/use-cypress-for-e2e-testing.html-450ffdcd.js @@ -1,4 +1,4 @@ -import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o,c,a as s,b as n,d as e,f as t}from"./app-b649ee34.js";const l={},r=t('

使用 Cypress 进行端对端测试

为什么写端对端测试

写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。

谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。

为什么用 Cypress

',5),d={href:"https://docs.cypress.io/guides/overview/why-cypress.html",target:"_blank",rel:"noopener noreferrer"},u=t(`

缺点:全英文档

快速开始

安装

yarn add cypress -D
+import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o,c,a as s,b as n,d as e,f as t}from"./app-a9d55428.js";const l={},r=t('

使用 Cypress 进行端对端测试

为什么写端对端测试

写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。

谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。

为什么用 Cypress

',5),d={href:"https://docs.cypress.io/guides/overview/why-cypress.html",target:"_blank",rel:"noopener noreferrer"},u=t(`

缺点:全英文档

快速开始

安装

yarn add cypress -D
 
`,4),v={href:"https://docs.cypress.io/guides/getting-started/installing-cypress.html#npm-install",target:"_blank",rel:"noopener noreferrer"},m=t(`

一般而言,国内用户都会在上述过程中卡住,最好在命令行设置网络代理后再下载(懂的自然懂)。

export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=socks5://127.0.0.1:7890
 

如果是在 CI 环境,记得缓存 cypress binary。

安装完后,修改 package.json

  "scripts": {
     "e2e": "cypress open"
diff --git a/assets/use-jest-for-test-driven-development.html-842be257.js b/assets/use-jest-for-test-driven-development.html-d2d469d3.js
similarity index 89%
rename from assets/use-jest-for-test-driven-development.html-842be257.js
rename to assets/use-jest-for-test-driven-development.html-d2d469d3.js
index bfc8077d..df6628a3 100644
--- a/assets/use-jest-for-test-driven-development.html-842be257.js
+++ b/assets/use-jest-for-test-driven-development.html-d2d469d3.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-efcacba2","path":"/software-testing/use-jest-for-test-driven-development.html","title":"使用 Jest 实践测试驱动开发","lang":"zh-CN","frontmatter":{"date":"2019-04-21T00:00:00.000Z","tag":["Node.js","Testing"],"description":"使用 Jest 实践测试驱动开发 前言 本文将使用jest进行测试驱动开发的示例,源码在github。 旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-jest-for-test-driven-development.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 Jest 实践测试驱动开发"}],["meta",{"property":"og:description","content":"使用 Jest 实践测试驱动开发 前言 本文将使用jest进行测试驱动开发的示例,源码在github。 旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2019-04-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 Jest 实践测试驱动开发\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2019-04-21T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"环境搭建","slug":"环境搭建","link":"#环境搭建","children":[]},{"level":2,"title":"开发","slug":"开发","link":"#开发","children":[{"level":3,"title":"文件初始化","slug":"文件初始化","link":"#文件初始化","children":[]},{"level":3,"title":"第一个用例","slug":"第一个用例","link":"#第一个用例","children":[]},{"level":3,"title":"第二个用例","slug":"第二个用例","link":"#第二个用例","children":[]},{"level":3,"title":"第三个用例","slug":"第三个用例","link":"#第三个用例","children":[]},{"level":3,"title":"第四个用例","slug":"第四个用例","link":"#第四个用例","children":[]},{"level":3,"title":"第五个用例","slug":"第五个用例","link":"#第五个用例","children":[]},{"level":3,"title":"重构","slug":"重构","link":"#重构","children":[]}]},{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":6.26,"words":1879},"filePathRelative":"software-testing/use-jest-for-test-driven-development.md","localizedDate":"2019年4月21日","excerpt":"

使用 Jest 实践测试驱动开发

\\n

前言

\\n

本文将使用jest进行测试驱动开发的示例,源码在github
\\n旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-efcacba2","path":"/software-testing/use-jest-for-test-driven-development.html","title":"使用 Jest 实践测试驱动开发","lang":"zh-CN","frontmatter":{"date":"2019-04-21T00:00:00.000Z","tag":["Node.js","Testing"],"description":"使用 Jest 实践测试驱动开发 前言 本文将使用jest进行测试驱动开发的示例,源码在github。 旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-jest-for-test-driven-development.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 Jest 实践测试驱动开发"}],["meta",{"property":"og:description","content":"使用 Jest 实践测试驱动开发 前言 本文将使用jest进行测试驱动开发的示例,源码在github。 旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2019-04-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 Jest 实践测试驱动开发\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2019-04-21T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"环境搭建","slug":"环境搭建","link":"#环境搭建","children":[]},{"level":2,"title":"开发","slug":"开发","link":"#开发","children":[{"level":3,"title":"文件初始化","slug":"文件初始化","link":"#文件初始化","children":[]},{"level":3,"title":"第一个用例","slug":"第一个用例","link":"#第一个用例","children":[]},{"level":3,"title":"第二个用例","slug":"第二个用例","link":"#第二个用例","children":[]},{"level":3,"title":"第三个用例","slug":"第三个用例","link":"#第三个用例","children":[]},{"level":3,"title":"第四个用例","slug":"第四个用例","link":"#第四个用例","children":[]},{"level":3,"title":"第五个用例","slug":"第五个用例","link":"#第五个用例","children":[]},{"level":3,"title":"重构","slug":"重构","link":"#重构","children":[]}]},{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":6.26,"words":1879},"filePathRelative":"software-testing/use-jest-for-test-driven-development.md","localizedDate":"2019年4月21日","excerpt":"

使用 Jest 实践测试驱动开发

\\n

前言

\\n

本文将使用jest进行测试驱动开发的示例,源码在github
\\n旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。

","autoDesc":true}');export{e as data}; diff --git a/assets/use-jest-for-test-driven-development.html-8fbc9f0a.js b/assets/use-jest-for-test-driven-development.html-ed10dede.js similarity index 99% rename from assets/use-jest-for-test-driven-development.html-8fbc9f0a.js rename to assets/use-jest-for-test-driven-development.html-ed10dede.js index dd8a2202..59a5bd4a 100644 --- a/assets/use-jest-for-test-driven-development.html-8fbc9f0a.js +++ b/assets/use-jest-for-test-driven-development.html-ed10dede.js @@ -1,4 +1,4 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o,c as l,a as s,b as n,d as e,f as c}from"./app-b649ee34.js";const i={},u=s("h1",{id:"使用-jest-实践测试驱动开发",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#使用-jest-实践测试驱动开发","aria-hidden":"true"},"#"),n(" 使用 Jest 实践测试驱动开发")],-1),r=s("h2",{id:"前言",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),n(" 前言")],-1),d={href:"https://jestjs.io/docs/en/getting-started",target:"_blank",rel:"noopener noreferrer"},k={href:"https://github.com/levy9527/jest-tdd-demo",target:"_blank",rel:"noopener noreferrer"},v=s("br",null,null,-1),m=c(`

本文的重点是过程以及思维方法,框架以及用法不是重点。

本文使用的编程语言是javascript,思路对其他语言也是适用的。

本文主要以函数作为测试对象。

环境搭建

假设项目结构为

.
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o,c as l,a as s,b as n,d as e,f as c}from"./app-a9d55428.js";const i={},u=s("h1",{id:"使用-jest-实践测试驱动开发",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#使用-jest-实践测试驱动开发","aria-hidden":"true"},"#"),n(" 使用 Jest 实践测试驱动开发")],-1),r=s("h2",{id:"前言",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),n(" 前言")],-1),d={href:"https://jestjs.io/docs/en/getting-started",target:"_blank",rel:"noopener noreferrer"},k={href:"https://github.com/levy9527/jest-tdd-demo",target:"_blank",rel:"noopener noreferrer"},v=s("br",null,null,-1),m=c(`

本文的重点是过程以及思维方法,框架以及用法不是重点。

本文使用的编程语言是javascript,思路对其他语言也是适用的。

本文主要以函数作为测试对象。

环境搭建

假设项目结构为

.
 ├── README.md
 ├── package.json
 ├── src
diff --git a/assets/use-playwright-for-ui-testing.html-44356ace.js b/assets/use-playwright-for-ui-testing.html-f3b691a5.js
similarity index 99%
rename from assets/use-playwright-for-ui-testing.html-44356ace.js
rename to assets/use-playwright-for-ui-testing.html-f3b691a5.js
index 488cd353..7ff8c1a6 100644
--- a/assets/use-playwright-for-ui-testing.html-44356ace.js
+++ b/assets/use-playwright-for-ui-testing.html-f3b691a5.js
@@ -1,4 +1,4 @@
-import{_ as c}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o as i,c as l,a as s,b as n,d as a,w as u,f as e}from"./app-b649ee34.js";const r={},d=e(`

下一代 UI 自动化测试工具 Playwright

前言

Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有:

  1. 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具
  2. 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件)

另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。

综上所述,笔者认为 Playwright 是值得在研发过程中引入的一款测试工具,它可以帮助研发、测试团队较平滑地走上自动化测试之路。它适用的典型场景之一,就是做回归测试——测试人员再也不用在界面上使用鼠标进行“点点点”,解放双手,提高测试效率。

安装

yarn create playwright
+import{_ as c}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o as i,c as l,a as s,b as n,d as a,w as u,f as e}from"./app-a9d55428.js";const r={},d=e(`

下一代 UI 自动化测试工具 Playwright

前言

Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有:

  1. 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具
  2. 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件)

另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。

综上所述,笔者认为 Playwright 是值得在研发过程中引入的一款测试工具,它可以帮助研发、测试团队较平滑地走上自动化测试之路。它适用的典型场景之一,就是做回归测试——测试人员再也不用在界面上使用鼠标进行“点点点”,解放双手,提高测试效率。

安装

yarn create playwright
 

根据命令提示,输入如下:
image.png
默认会下载所有浏览器,如果没有浏览器兼容性测试的需求,推荐如上图所示,手动安装一个浏览器。

以安装 chromium 为例,相应操作步骤如下:

  1. 修改配置
vi playwright.config.ts
 

注释掉以下内容:
image.png

  1. 安装浏览器
yarn playwright install --with-deps chromium
 

等待一段时间即可,如果失败,请重试。
image.png

`,16),k={href:"https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright",target:"_blank",rel:"noopener noreferrer"},g=e(`

使用

代码生成

虽然可以参考 example.spec.ts去编写测试用例,但这不是 Playwright 独特之处。Playwright 最引入注目的,是代码生成功能。

yarn playwright codegen
diff --git a/assets/use-playwright-for-ui-testing.html-39747b36.js b/assets/use-playwright-for-ui-testing.html-fba57f32.js
similarity index 94%
rename from assets/use-playwright-for-ui-testing.html-39747b36.js
rename to assets/use-playwright-for-ui-testing.html-fba57f32.js
index 76e23751..80f430f7 100644
--- a/assets/use-playwright-for-ui-testing.html-39747b36.js
+++ b/assets/use-playwright-for-ui-testing.html-fba57f32.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-0be1af08","path":"/software-testing/use-playwright-for-ui-testing.html","title":"下一代 UI 自动化测试工具 Playwright","lang":"zh-CN","frontmatter":{"date":"2023-05-07T00:00:00.000Z","tag":["Node.js","Python","Testing"],"description":"下一代 UI 自动化测试工具 Playwright 前言 Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有: 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件) 另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-playwright-for-ui-testing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"下一代 UI 自动化测试工具 Playwright"}],["meta",{"property":"og:description","content":"下一代 UI 自动化测试工具 Playwright 前言 Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有: 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件) 另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2023-05-07T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"下一代 UI 自动化测试工具 Playwright\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-05-07T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[{"level":3,"title":"代码生成","slug":"代码生成","link":"#代码生成","children":[]},{"level":3,"title":"修改代码","slug":"修改代码","link":"#修改代码","children":[]},{"level":3,"title":"执行用例","slug":"执行用例","link":"#执行用例","children":[]},{"level":3,"title":"调试用例","slug":"调试用例","link":"#调试用例","children":[]},{"level":3,"title":"查看报告","slug":"查看报告","link":"#查看报告","children":[]}]},{"level":2,"title":"常见场景与解决方案","slug":"常见场景与解决方案","link":"#常见场景与解决方案","children":[{"level":3,"title":"应用登录","slug":"应用登录","link":"#应用登录","children":[]},{"level":3,"title":"环境变量","slug":"环境变量","link":"#环境变量","children":[]},{"level":3,"title":"超时时间","slug":"超时时间","link":"#超时时间","children":[]},{"level":3,"title":"元素选择","slug":"元素选择","link":"#元素选择","children":[]},{"level":3,"title":"声明断言 && 检查元素是否存在","slug":"声明断言-检查元素是否存在","link":"#声明断言-检查元素是否存在","children":[]},{"level":3,"title":"获取第n个元素","slug":"获取第n个元素","link":"#获取第n个元素","children":[]},{"level":3,"title":"遍历元素","slug":"遍历元素","link":"#遍历元素","children":[]},{"level":3,"title":"获取元素属性","slug":"获取元素属性","link":"#获取元素属性","children":[]},{"level":3,"title":"判断子元素数量","slug":"判断子元素数量","link":"#判断子元素数量","children":[]},{"level":3,"title":"鼠标悬浮","slug":"鼠标悬浮","link":"#鼠标悬浮","children":[]},{"level":3,"title":"操作剪贴板","slug":"操作剪贴板","link":"#操作剪贴板","children":[]}]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[]},{"level":2,"title":"其他","slug":"其他","link":"#其他","children":[{"level":3,"title":"setup.py bdist_wheel did not run successfully","slug":"setup-py-bdist-wheel-did-not-run-successfully","link":"#setup-py-bdist-wheel-did-not-run-successfully","children":[]}]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":10,"words":3001},"filePathRelative":"software-testing/use-playwright-for-ui-testing.md","localizedDate":"2023年5月7日","excerpt":"

下一代 UI 自动化测试工具 Playwright

\\n

前言

\\n

Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有:

\\n
    \\n
  1. 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具
  2. \\n
  3. 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件)
  4. \\n
\\n

另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-0be1af08","path":"/software-testing/use-playwright-for-ui-testing.html","title":"下一代 UI 自动化测试工具 Playwright","lang":"zh-CN","frontmatter":{"date":"2023-05-07T00:00:00.000Z","tag":["Node.js","Python","Testing"],"description":"下一代 UI 自动化测试工具 Playwright 前言 Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有: 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件) 另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-playwright-for-ui-testing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"下一代 UI 自动化测试工具 Playwright"}],["meta",{"property":"og:description","content":"下一代 UI 自动化测试工具 Playwright 前言 Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有: 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件) 另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2023-05-07T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"下一代 UI 自动化测试工具 Playwright\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-05-07T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[{"level":3,"title":"代码生成","slug":"代码生成","link":"#代码生成","children":[]},{"level":3,"title":"修改代码","slug":"修改代码","link":"#修改代码","children":[]},{"level":3,"title":"执行用例","slug":"执行用例","link":"#执行用例","children":[]},{"level":3,"title":"调试用例","slug":"调试用例","link":"#调试用例","children":[]},{"level":3,"title":"查看报告","slug":"查看报告","link":"#查看报告","children":[]}]},{"level":2,"title":"常见场景与解决方案","slug":"常见场景与解决方案","link":"#常见场景与解决方案","children":[{"level":3,"title":"应用登录","slug":"应用登录","link":"#应用登录","children":[]},{"level":3,"title":"环境变量","slug":"环境变量","link":"#环境变量","children":[]},{"level":3,"title":"超时时间","slug":"超时时间","link":"#超时时间","children":[]},{"level":3,"title":"元素选择","slug":"元素选择","link":"#元素选择","children":[]},{"level":3,"title":"声明断言 && 检查元素是否存在","slug":"声明断言-检查元素是否存在","link":"#声明断言-检查元素是否存在","children":[]},{"level":3,"title":"获取第n个元素","slug":"获取第n个元素","link":"#获取第n个元素","children":[]},{"level":3,"title":"遍历元素","slug":"遍历元素","link":"#遍历元素","children":[]},{"level":3,"title":"获取元素属性","slug":"获取元素属性","link":"#获取元素属性","children":[]},{"level":3,"title":"判断子元素数量","slug":"判断子元素数量","link":"#判断子元素数量","children":[]},{"level":3,"title":"鼠标悬浮","slug":"鼠标悬浮","link":"#鼠标悬浮","children":[]},{"level":3,"title":"操作剪贴板","slug":"操作剪贴板","link":"#操作剪贴板","children":[]}]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[]},{"level":2,"title":"其他","slug":"其他","link":"#其他","children":[{"level":3,"title":"setup.py bdist_wheel did not run successfully","slug":"setup-py-bdist-wheel-did-not-run-successfully","link":"#setup-py-bdist-wheel-did-not-run-successfully","children":[]}]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":10,"words":3001},"filePathRelative":"software-testing/use-playwright-for-ui-testing.md","localizedDate":"2023年5月7日","excerpt":"

下一代 UI 自动化测试工具 Playwright

\\n

前言

\\n

Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有:

\\n
    \\n
  1. 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具
  2. \\n
  3. 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件)
  4. \\n
\\n

另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。

","autoDesc":true}');export{e as data}; diff --git a/assets/use-postman-for-api-testing.html-69020f9d.js b/assets/use-postman-for-api-testing.html-e4659ca6.js similarity index 93% rename from assets/use-postman-for-api-testing.html-69020f9d.js rename to assets/use-postman-for-api-testing.html-e4659ca6.js index 36a66174..e339363d 100644 --- a/assets/use-postman-for-api-testing.html-69020f9d.js +++ b/assets/use-postman-for-api-testing.html-e4659ca6.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-75616b85","path":"/software-testing/use-postman-for-api-testing.html","title":"使用 Postman 进行 API 测试","lang":"zh-CN","frontmatter":{"date":"2023-09-13T00:00:00.000Z","tag":["Node.js","Daily"],"description":"使用 Postman 进行 API 测试 前言 虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。 而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。 还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-postman-for-api-testing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 Postman 进行 API 测试"}],["meta",{"property":"og:description","content":"使用 Postman 进行 API 测试 前言 虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。 而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。 还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-13T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 Postman 进行 API 测试\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-13T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"本地调试","slug":"本地调试","link":"#本地调试","children":[{"level":3,"title":"接口集合","slug":"接口集合","link":"#接口集合","children":[]},{"level":3,"title":"从 cURL 导入","slug":"从-curl-导入","link":"#从-curl-导入","children":[]},{"level":3,"title":"环境变量","slug":"环境变量","link":"#环境变量","children":[]},{"level":3,"title":"请求设置","slug":"请求设置","link":"#请求设置","children":[]}]},{"level":2,"title":"接口测试","slug":"接口测试","link":"#接口测试","children":[{"level":3,"title":"编写用例","slug":"编写用例","link":"#编写用例","children":[]},{"level":3,"title":"上传文件","slug":"上传文件","link":"#上传文件","children":[]},{"level":3,"title":"运行集合","slug":"运行集合","link":"#运行集合","children":[]}]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[{"level":3,"title":"导出接口","slug":"导出接口","link":"#导出接口","children":[]},{"level":3,"title":"导出环境变量","slug":"导出环境变量","link":"#导出环境变量","children":[]},{"level":3,"title":"复制要上传的文件","slug":"复制要上传的文件","link":"#复制要上传的文件","children":[]},{"level":3,"title":"提交到Git","slug":"提交到git","link":"#提交到git","children":[]},{"level":3,"title":"建立CI任务","slug":"建立ci任务","link":"#建立ci任务","children":[]}]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5.41,"words":1623},"filePathRelative":"software-testing/use-postman-for-api-testing.md","localizedDate":"2023年9月13日","excerpt":"

使用 Postman 进行 API 测试

\\n

前言

\\n

虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。

\\n

而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。

\\n

还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-75616b85","path":"/software-testing/use-postman-for-api-testing.html","title":"使用 Postman 进行 API 测试","lang":"zh-CN","frontmatter":{"date":"2023-09-13T00:00:00.000Z","tag":["Node.js","Daily"],"description":"使用 Postman 进行 API 测试 前言 虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。 而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。 还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-postman-for-api-testing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 Postman 进行 API 测试"}],["meta",{"property":"og:description","content":"使用 Postman 进行 API 测试 前言 虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。 而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。 还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-13T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 Postman 进行 API 测试\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-13T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"本地调试","slug":"本地调试","link":"#本地调试","children":[{"level":3,"title":"接口集合","slug":"接口集合","link":"#接口集合","children":[]},{"level":3,"title":"从 cURL 导入","slug":"从-curl-导入","link":"#从-curl-导入","children":[]},{"level":3,"title":"环境变量","slug":"环境变量","link":"#环境变量","children":[]},{"level":3,"title":"请求设置","slug":"请求设置","link":"#请求设置","children":[]}]},{"level":2,"title":"接口测试","slug":"接口测试","link":"#接口测试","children":[{"level":3,"title":"编写用例","slug":"编写用例","link":"#编写用例","children":[]},{"level":3,"title":"上传文件","slug":"上传文件","link":"#上传文件","children":[]},{"level":3,"title":"运行集合","slug":"运行集合","link":"#运行集合","children":[]}]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[{"level":3,"title":"导出接口","slug":"导出接口","link":"#导出接口","children":[]},{"level":3,"title":"导出环境变量","slug":"导出环境变量","link":"#导出环境变量","children":[]},{"level":3,"title":"复制要上传的文件","slug":"复制要上传的文件","link":"#复制要上传的文件","children":[]},{"level":3,"title":"提交到Git","slug":"提交到git","link":"#提交到git","children":[]},{"level":3,"title":"建立CI任务","slug":"建立ci任务","link":"#建立ci任务","children":[]}]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5.51,"words":1653},"filePathRelative":"software-testing/use-postman-for-api-testing.md","localizedDate":"2023年9月13日","excerpt":"

使用 Postman 进行 API 测试

\\n

前言

\\n

虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。

\\n

而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。

\\n

还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/use-postman-for-api-testing.html-557f4cb8.js b/assets/use-postman-for-api-testing.html-ed796a62.js similarity index 92% rename from assets/use-postman-for-api-testing.html-557f4cb8.js rename to assets/use-postman-for-api-testing.html-ed796a62.js index c5bdc604..7361066e 100644 --- a/assets/use-postman-for-api-testing.html-557f4cb8.js +++ b/assets/use-postman-for-api-testing.html-ed796a62.js @@ -1,4 +1,4 @@ -import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c as p,e,a as n,b as a,f as o}from"./app-b649ee34.js";const c={},i=n("h1",{id:"使用-postman-进行-api-测试",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#使用-postman-进行-api-测试","aria-hidden":"true"},"#"),a(" 使用 Postman 进行 API 测试")],-1),l=n("h2",{id:"前言",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),a(" 前言")],-1),u=n("p",null,"虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。",-1),r=n("p",null,"而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。",-1),d=n("p",null,"还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:",-1),k=o(`
// 为什么不设置 Map<String, String> ? 
+import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c as p,e,a as n,b as a,f as o}from"./app-a9d55428.js";const c={},i=n("h1",{id:"使用-postman-进行-api-测试",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#使用-postman-进行-api-测试","aria-hidden":"true"},"#"),a(" 使用 Postman 进行 API 测试")],-1),l=n("h2",{id:"前言",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),a(" 前言")],-1),u=n("p",null,"虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。",-1),r=n("p",null,"而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。",-1),d=n("p",null,"还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:",-1),k=o(`
// 为什么不设置 Map<String, String> ? 
 // 因为 menu 有个字段的类型是 Integer,使用 String 接收运行时会报错。
 Optional<Map<String, Object>> foundMenu = menuList.stream().filter(v -> {
   String code = (String) v.get("name"); // 这种类型转换代码实在多余!
@@ -10,7 +10,10 @@ Optional<Map
     "datasourceTypeId": {{datasourceTypeId}}, // 这个是数字
     "fileId": "{{fileId}}", // 这个是字符串
 }
-

环境变量还可以在测试用例里去修改值:

请求设置

对于请求体的发送,一般进行如下设置:

还可以利用 Pre-request Script, 在请求前动态修改请求设置。
比如有以下场景:

  • 线上环境接口路径为:/app-name/api/v1/api-name
  • 本地环境接口路径为:/v1/api-name,也即线上环境接口地址多了两个前缀

观察请求在 Postman 中的数据结构如下:

则可以编写前置脚本如下:

if (pm.environment.name.includes("local")) {
+

Postman内置了全局变量,输入 {{ 会出现提示:

使用示例:

{
+  "name": "my_name_{{$timestamp}}"
+}
+

环境变量还可以在测试用例里去修改值:

请求设置

对于请求体的发送,一般进行如下设置:

还可以利用 Pre-request Script, 在请求前动态修改请求设置。
比如有以下场景:

  • 线上环境接口路径为:/app-name/api/v1/api-name
  • 本地环境接口路径为:/v1/api-name,也即线上环境接口地址多了两个前缀

观察请求在 Postman 中的数据结构如下:

则可以编写前置脚本如下:

if (pm.environment.name.includes("local")) {
     pm.request.url.path.shift()
     pm.request.url.path.shift()
 }
@@ -62,4 +65,4 @@ Optional<Map
     - your-gitlab-runner #记得把这里修改成你的 runner 名字
   only:
     - /dev|test|uat/
-

推送代码,即可看到流水线

结果如下:

`,78);function m(g,v){return t(),p("div",null,[i,l,u,r,d,e(" more "),k])}const f=s(c,[["render",m],["__file","use-postman-for-api-testing.html.vue"]]);export{f as default}; +

推送代码,即可看到流水线

结果如下:

`,81);function m(g,v){return t(),p("div",null,[i,l,u,r,d,e(" more "),k])}const f=s(c,[["render",m],["__file","use-postman-for-api-testing.html.vue"]]);export{f as default}; diff --git a/assets/using-enum-in-java.html-cb86be54.js b/assets/using-enum-in-java.html-cb86be54.js new file mode 100644 index 00000000..cb8b6cfe --- /dev/null +++ b/assets/using-enum-in-java.html-cb86be54.js @@ -0,0 +1,74 @@ +import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as c,c as l,a as n,b as s,d as e,f as t}from"./app-a9d55428.js";const i={},u=t(`

枚举的推荐实践

背景

定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。

而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。

本文推荐,不要在枚举中定义数字,直接使用枚举名即可!

Java

极简实现

理想状态下,枚举就应该这样简单!

public enum SEX {
+  MALE,
+  FEMALE;
+}
+

常见实现

然而,代码库中常见的实现是,使用 enum 关键字定义枚举类型,并写下枚举名以及相应的绑定值

public enum SEX {
+  MALE(1, "this is description for male"),
+  FEMALE(0, "this is description for female"); 
+}
+
+

就这点代码,从业务的角度讲,逻辑已经实现了。

但从具体编程语言(Java)的角度讲,工作还没有完成:此时,编译器会提示报错,因为缺少构造函数。

站在调用方的角度,就会发现,我们没有方法拿到枚举里的定义值,也即(1, "this is description for male")

因此,还需要编写以下内容:

  • final 的成员变量
  • 在构造函数中为成员变量赋值
  • 定义成员变量相应的 getter 方法
  private final Integer code;
+  private final String desc;
+
+  MyEnum(int code, String desc) {
+    this.code = code;
+    this.desc = desc;
+  }
+
+  public Integer getCode() {
+    return code;
+  }
+
+  public String getDesc() {
+    return desc;
+  }
+

前面说到,我们期望枚举里有 key-value 的功能,则还要再定义 getValueByKey 方法:

  public static String getDescByCode(Integer code) {
+    for (MyEnum e : values()) {
+      if (e.getCode().equals(code)) return e.getDesc();
+    }
+    return "";
+  }
+

有了上面的方法,我们才可以通过传入 code,返回相应的 desc

如果反过来,我们想通过传入 desc 返回相应的 code 呢?还得再写一个方法!真是烦琐!

更好的方式

前面我们可以看到,当关键逻辑写出来以后,还要写那么多模板代码,简直索然无味。

为什么把简单的事情搞复杂,有没有更好的方式?

需要了解到以下事实,枚举类型提供了通过枚举名获得枚举值的方法:

  public static void main(String[] args) {
+    MyEnum myEnum = MyEnum.valueOf("MALE");
+    System.out.println(myEnum.getDesc());
+  }
+

也就是说,如果使用 valueOf的方式来定位枚举值,就可以通过 getter 方法来获取自定义的业务值,不需要再写自定义的 getValueByKey 方法。

为什么还要定义数字?

另外,再看之前的枚举定义,为什么要定义 MALE(1, "this is description for male")呢?
MALE本身已经有含义的情况下,为何还要再设置一个数字呢?

这个数字导致了不必要的转换:

  1. 前端传数字 -> 后端转成枚举
  2. 后端再把枚举转成数字 -> 存入数据库

最蛋疼的就是,select 数据库数据的时候,全是1、2、3,都不知道什么意思。

能不能不要这个数字? 直接把定义好的枚举名,存入数据库呢?

表面上看,这是因为数据库定义如此——从数据库查出来是数字,因而要根据数字去获取别的业务信息。

然而,我再问了一下,得到的回答是: 上述做法是设计如此。为了节省存储空间及提升查询性能,才在数据库设置的数字,而不是直接存储字符串。

这就引出了另一个问题,需要这样做来提高性能吗?对此,我们接下来要进行数据库层面的讨论。

数据库

数据库本身是支持检举类型的,下文以 MySQL 为例进行说明。

枚举相关的 SQL 语句示例:

-- 建表
+CREATE TABLE shirts (
+  name VARCHAR(40),
+  size ENUM('x-small', 'small', 'medium', 'large', 'x-large')
+);
+
+-- 插入
+INSERT INTO shirts (name, size) VALUES ('dress shirt','large'), ('t-shirt','medium'),
+('polo shirt','small');
+
+-- 查询
+SELECT name, size FROM shirts WHERE size = 'medium';
++---------+--------+
+| name    | size   |
++---------+--------+
+| t-shirt | medium |
++---------+--------+
+
`,41),r=n("br",null,null,-1),d={href:"https://dev.mysql.com/doc/refman/5.7/en/storage-requirements.html#data-types-storage-reqs-numeric",target:"_blank",rel:"noopener noreferrer"},k=t(`

由上可知,为了节省空间及提高查询性能,在数据库层面使用数字代表枚举进行存储,是不必要的,因为数据库本身已有相应的功能。

排序特点

排序规则如下(如执行order by 时):

  • NULL 在最前面,'' 次之,接下来是非空的枚举值
  • 定义枚举值时的顺序,就是排序的顺序

推荐使用以下技巧:

  • 按字母表顺序定义检举值
  • 把检举值转换成字符串再排序 ORDER BY CAST(col AS CHAR) or ORDER BY CONCAT(col)

添加新值

使用枚举类型的最大的问题是,后续添加新值时需要执行 alter table。

如果枚举值经常变动且对枚举值的顺序要求(添加的新值不一定在最后面),则不建议使用枚举类型。

否则的话,可以使用枚举类型。

因为 alter table 的机制是:

  1. 创建临时表 t'
  2. 插入数据
  3. 删除当前表 t
  4. 把 t' 重命名为 t

这在表数据量较大时,会导致表被锁较长时间而不可用。

可以使用以下技巧进行更新,记得严格按照顺序执行:

CREATE TABLE database.shirts_tmp LIKE database.shirts;
+-- 在最后添加了 'xx-large'
+ALTER TABLE database.shirts_tmp MODIFY COLUMN ENUM('x-small', 'small', 'medium', 'large', 'x-large', 'xx-large');
+
+FLUSH TABLES WITH READ LOCK;
+
+SHOW variables LIKE 'datadir';
+SELECT DATABASE() as databaseName;
+
# 登录 MySQL 所在机器
+# 根据上面最后两行SQL,进入相关目录
+cd \${datadir}/\${databaseName}
+
+mv shirts shirts_old;
+mv shirts_tmp shirts;
+mv shirts_old shirts_tmp;
+
UNLOCK TABLES;
+
+-- 可以在删除前检查两张表的定义是否已交换
+-- select * from database.shirts;
+-- select * from database.shirts_tmp;
+
+DROP TABLE database.shirts_tmp;
+

如果在 Navicat 或 DBeaver等图形工具上看不出表结构的变化,请刷新数据库。

总结

本文主要目的是想消除枚举中对魔法数字的误用,试图让 Java 代码、数据库数据、以及前端 API 传参都使用可理解、可读性强的枚举值。

在数据库层面,对于枚举类型字段的注意点,本文也做了说明。如果实在不想每次添加新的枚举值都执行 alter table语句,贪图省事,使用 varchar 存储也未尝不可。

总之,本文想强调的是 Java 代码与数据库数据展示内容的一致性,至于数据库的存储格式,是见仁见智的。

参考资料

`,23),v={href:"https://www.baeldung.com/java-enum-values",target:"_blank",rel:"noopener noreferrer"},m={href:"https://www.baeldung.com/a-guide-to-java-enums",target:"_blank",rel:"noopener noreferrer"},b={href:"https://dev.mysql.com/doc/refman/5.7/en/enum.html",target:"_blank",rel:"noopener noreferrer"},h={href:"https://www.oreilly.com/library/view/high-performance-mysql/9781449332471/",target:"_blank",rel:"noopener noreferrer"};function g(y,w){const a=o("ExternalLinkIcon");return c(),l("div",null,[u,n("p",null,[s("存储方式:转换成数字存储,查询时再转换成字符串"),r,n("a",d,[s("存储空间"),e(a)]),s(":1 or 2 bytes (65,535 values maximum)")]),k,n("ul",null,[n("li",null,[n("a",v,[s("https://www.baeldung.com/java-enum-values"),e(a)])]),n("li",null,[n("a",m,[s("https://www.baeldung.com/a-guide-to-java-enums"),e(a)])]),n("li",null,[n("a",b,[s("https://dev.mysql.com/doc/refman/5.7/en/enum.html"),e(a)])]),n("li",null,[n("a",h,[s("High Performance MySQL 3rd Edition"),e(a)])])])])}const _=p(i,[["render",g],["__file","using-enum-in-java.html.vue"]]);export{_ as default}; diff --git a/assets/using-enum-in-java.html-db9b25e7.js b/assets/using-enum-in-java.html-db9b25e7.js new file mode 100644 index 00000000..f0f382a3 --- /dev/null +++ b/assets/using-enum-in-java.html-db9b25e7.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-2ba0b01e","path":"/java/using-enum-in-java.html","title":"枚举的推荐实践","lang":"zh-CN","frontmatter":{"date":"2022-10-14T00:00:00.000Z","tag":["Java","Daily"],"description":"枚举的推荐实践 背景 定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。 而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。 本文推荐,不要在枚举中定义数字,直接使用枚举名即可! Java 极简实现 理想状态下,枚举就应该这样简单! public enum SEX { MALE, FEMALE; }","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/using-enum-in-java.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"枚举的推荐实践"}],["meta",{"property":"og:description","content":"枚举的推荐实践 背景 定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。 而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。 本文推荐,不要在枚举中定义数字,直接使用枚举名即可! Java 极简实现 理想状态下,枚举就应该这样简单! public enum SEX { MALE, FEMALE; }"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-10-14T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"枚举的推荐实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-10-14T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"Java","slug":"java","link":"#java","children":[{"level":3,"title":"极简实现","slug":"极简实现","link":"#极简实现","children":[]},{"level":3,"title":"常见实现","slug":"常见实现","link":"#常见实现","children":[]},{"level":3,"title":"更好的方式","slug":"更好的方式","link":"#更好的方式","children":[]},{"level":3,"title":"为什么还要定义数字?","slug":"为什么还要定义数字","link":"#为什么还要定义数字","children":[]}]},{"level":2,"title":"数据库","slug":"数据库","link":"#数据库","children":[{"level":3,"title":"排序特点","slug":"排序特点","link":"#排序特点","children":[]},{"level":3,"title":"添加新值","slug":"添加新值","link":"#添加新值","children":[]}]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]},{"level":2,"title":"参考资料","slug":"参考资料","link":"#参考资料","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5.59,"words":1676},"filePathRelative":"java/using-enum-in-java.md","localizedDate":"2022年10月14日","excerpt":"

枚举的推荐实践

\\n

背景

\\n

定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。

\\n

而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。

\\n

本文推荐,不要在枚举中定义数字,直接使用枚举名即可!

\\n

Java

\\n

极简实现

\\n

理想状态下,枚举就应该这样简单!

\\n
public enum SEX {\\n  MALE,\\n  FEMALE;\\n}\\n
","autoDesc":true}');export{e as data}; diff --git a/assets/vim-creator-pass-away.html-c6774dfb.js b/assets/vim-creator-pass-away.html-89c669cf.js similarity index 91% rename from assets/vim-creator-pass-away.html-c6774dfb.js rename to assets/vim-creator-pass-away.html-89c669cf.js index 774cf973..43fc6e46 100644 --- a/assets/vim-creator-pass-away.html-c6774dfb.js +++ b/assets/vim-creator-pass-away.html-89c669cf.js @@ -1 +1 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as a,o as i,c as s,d as r,a as e,b as n}from"./app-b649ee34.js";const c={},_=e("h1",{id:"vim-作者离世",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#vim-作者离世","aria-hidden":"true"},"#"),n(" Vim 作者离世")],-1),l=e("p",null,"R.I.P 🙏",-1),d=e("p",null,"Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。",-1),m=e("p",null,"这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。",-1);function h(p,f){const o=a("BiliBili");return i(),s("div",null,[_,l,d,m,r(o,{bvid:"BV1fu4y1q7qS"})])}const B=t(c,[["render",h],["__file","vim-creator-pass-away.html.vue"]]);export{B as default}; +import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as a,o as i,c as s,d as r,a as e,b as n}from"./app-a9d55428.js";const c={},_=e("h1",{id:"vim-作者离世",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#vim-作者离世","aria-hidden":"true"},"#"),n(" Vim 作者离世")],-1),l=e("p",null,"R.I.P 🙏",-1),d=e("p",null,"Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。",-1),m=e("p",null,"这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。",-1);function h(p,f){const o=a("BiliBili");return i(),s("div",null,[_,l,d,m,r(o,{bvid:"BV1fu4y1q7qS"})])}const B=t(c,[["render",h],["__file","vim-creator-pass-away.html.vue"]]);export{B as default}; diff --git a/assets/vim-creator-pass-away.html-1941f40c.js b/assets/vim-creator-pass-away.html-c4a8f7ca.js similarity index 83% rename from assets/vim-creator-pass-away.html-1941f40c.js rename to assets/vim-creator-pass-away.html-c4a8f7ca.js index e0976495..61218390 100644 --- a/assets/vim-creator-pass-away.html-1941f40c.js +++ b/assets/vim-creator-pass-away.html-c4a8f7ca.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-f1efc11c","path":"/daily/vim-creator-pass-away.html","title":"Vim 作者离世","lang":"zh-CN","frontmatter":{"date":"2023-08-06T00:00:00.000Z","tag":["Daily","Video"],"description":"Vim 作者离世 R.I.P 🙏 Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。 这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/vim-creator-pass-away.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Vim 作者离世"}],["meta",{"property":"og:description","content":"Vim 作者离世 R.I.P 🙏 Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。 这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-06T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Vim 作者离世\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-06T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.24,"words":71},"filePathRelative":"daily/vim-creator-pass-away.md","localizedDate":"2023年8月6日","excerpt":"

Vim 作者离世

\\n

R.I.P 🙏

\\n

Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。

\\n

这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-f1efc11c","path":"/daily/vim-creator-pass-away.html","title":"Vim 作者离世","lang":"zh-CN","frontmatter":{"date":"2023-08-06T00:00:00.000Z","tag":["Daily","Video"],"description":"Vim 作者离世 R.I.P 🙏 Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。 这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/vim-creator-pass-away.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Vim 作者离世"}],["meta",{"property":"og:description","content":"Vim 作者离世 R.I.P 🙏 Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。 这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-06T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Vim 作者离世\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-06T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.24,"words":71},"filePathRelative":"daily/vim-creator-pass-away.md","localizedDate":"2023年8月6日","excerpt":"

Vim 作者离世

\\n

R.I.P 🙏

\\n

Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。

\\n

这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/what-is-the-difference-between-sh-and-bash.html-c3e619d0.js b/assets/what-is-the-difference-between-sh-and-bash.html-38f57eba.js similarity index 84% rename from assets/what-is-the-difference-between-sh-and-bash.html-c3e619d0.js rename to assets/what-is-the-difference-between-sh-and-bash.html-38f57eba.js index 8210ef22..8ee063f8 100644 --- a/assets/what-is-the-difference-between-sh-and-bash.html-c3e619d0.js +++ b/assets/what-is-the-difference-between-sh-and-bash.html-38f57eba.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-414fd2b2","path":"/daily/what-is-the-difference-between-sh-and-bash.html","title":"sh与bash的区别","lang":"zh-CN","frontmatter":{"date":"2023-08-03T00:00:00.000Z","tag":["Linux","Docker","Video"],"description":"sh与bash的区别 结论:如果可移植性很重要,那么应该使用 sh!","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/what-is-the-difference-between-sh-and-bash.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"sh与bash的区别"}],["meta",{"property":"og:description","content":"sh与bash的区别 结论:如果可移植性很重要,那么应该使用 sh!"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Linux"}],["meta",{"property":"article:tag","content":"Docker"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-03T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"sh与bash的区别\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-03T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.83,"words":248},"filePathRelative":"daily/what-is-the-difference-between-sh-and-bash.md","localizedDate":"2023年8月3日","excerpt":"

sh与bash的区别

\\n

结论:如果可移植性很重要,那么应该使用 sh!

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-414fd2b2","path":"/daily/what-is-the-difference-between-sh-and-bash.html","title":"sh与bash的区别","lang":"zh-CN","frontmatter":{"date":"2023-08-03T00:00:00.000Z","tag":["Linux","Docker","Video"],"description":"sh与bash的区别 结论:如果可移植性很重要,那么应该使用 sh!","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/what-is-the-difference-between-sh-and-bash.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"sh与bash的区别"}],["meta",{"property":"og:description","content":"sh与bash的区别 结论:如果可移植性很重要,那么应该使用 sh!"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Linux"}],["meta",{"property":"article:tag","content":"Docker"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-03T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"sh与bash的区别\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-03T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.83,"words":248},"filePathRelative":"daily/what-is-the-difference-between-sh-and-bash.md","localizedDate":"2023年8月3日","excerpt":"

sh与bash的区别

\\n

结论:如果可移植性很重要,那么应该使用 sh!

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/what-is-the-difference-between-sh-and-bash.html-41ae2768.js b/assets/what-is-the-difference-between-sh-and-bash.html-f59545ff.js similarity index 94% rename from assets/what-is-the-difference-between-sh-and-bash.html-41ae2768.js rename to assets/what-is-the-difference-between-sh-and-bash.html-f59545ff.js index cd535327..070ff6bd 100644 --- a/assets/what-is-the-difference-between-sh-and-bash.html-41ae2768.js +++ b/assets/what-is-the-difference-between-sh-and-bash.html-f59545ff.js @@ -1 +1 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as r,c as n,e as s,d,a as e,b as i}from"./app-b649ee34.js";const l={},h=e("h1",{id:"sh与bash的区别",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#sh与bash的区别","aria-hidden":"true"},"#"),i(" sh与bash的区别")],-1),c=e("p",null,"结论:如果可移植性很重要,那么应该使用 sh!",-1),g=e("p",null,[i("以下是 ChatGPT 的回答:"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1691066962763-bfbe3c1a-cb4f-43bc-b181-062eabee9529.png#averageHue=%23e4e5e7&clientId=ue1ff8f12-10ba-4&from=paste&height=196&id=u6521b3f3&originHeight=392&originWidth=1428&originalType=binary&ratio=2&rotation=0&showTitle=false&size=235208&status=done&style=none&taskId=uef2009af-bd00-4e0e-a215-9cca4c37ed2&title=&width=714",alt:"",loading:"lazy"}),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1691067007160-ec9a619b-be1c-4947-9dfc-6578d7b95003.png#averageHue=%23e5e6e8&clientId=ue1ff8f12-10ba-4&from=paste&height=116&id=u443ed474&originHeight=232&originWidth=1354&originalType=binary&ratio=2&rotation=0&showTitle=false&size=75155&status=done&style=none&taskId=u999f43f5-eb71-425d-8651-fbe5098a7c9&title=&width=677",alt:"",loading:"lazy"}),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1691067065982-c0370621-ff74-41e2-a435-b21973c64be4.png#averageHue=%23e3e4e6&clientId=ue1ff8f12-10ba-4&from=paste&height=170&id=u00ae3745&originHeight=340&originWidth=1382&originalType=binary&ratio=2&rotation=0&showTitle=false&size=116797&status=done&style=none&taskId=u563107c4-dbc5-464b-bb87-ab1c1dea427&title=&width=691",alt:"",loading:"lazy"}),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1691067101217-a60e6bd8-600c-4c68-89d0-53675a11442c.png#averageHue=%23e5e5e8&clientId=ue1ff8f12-10ba-4&from=paste&height=118&id=u1559e5c7&originHeight=236&originWidth=1370&originalType=binary&ratio=2&rotation=0&showTitle=false&size=77383&status=done&style=none&taskId=u846749a0-6dcb-429b-ad44-0d6e77dea00&title=&width=685",alt:"",loading:"lazy"})],-1),m=e("p",null,"视频里有实战演示:",-1);function f(b,u){const a=o("BiliBili");return r(),n("div",null,[h,c,s(" more "),g,m,d(a,{bvid:"BV1Dj411676U"})])}const y=t(l,[["render",f],["__file","what-is-the-difference-between-sh-and-bash.html.vue"]]);export{y as default}; +import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as r,c as n,e as s,d,a as e,b as i}from"./app-a9d55428.js";const l={},h=e("h1",{id:"sh与bash的区别",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#sh与bash的区别","aria-hidden":"true"},"#"),i(" sh与bash的区别")],-1),c=e("p",null,"结论:如果可移植性很重要,那么应该使用 sh!",-1),g=e("p",null,[i("以下是 ChatGPT 的回答:"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1691066962763-bfbe3c1a-cb4f-43bc-b181-062eabee9529.png#averageHue=%23e4e5e7&clientId=ue1ff8f12-10ba-4&from=paste&height=196&id=u6521b3f3&originHeight=392&originWidth=1428&originalType=binary&ratio=2&rotation=0&showTitle=false&size=235208&status=done&style=none&taskId=uef2009af-bd00-4e0e-a215-9cca4c37ed2&title=&width=714",alt:"",loading:"lazy"}),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1691067007160-ec9a619b-be1c-4947-9dfc-6578d7b95003.png#averageHue=%23e5e6e8&clientId=ue1ff8f12-10ba-4&from=paste&height=116&id=u443ed474&originHeight=232&originWidth=1354&originalType=binary&ratio=2&rotation=0&showTitle=false&size=75155&status=done&style=none&taskId=u999f43f5-eb71-425d-8651-fbe5098a7c9&title=&width=677",alt:"",loading:"lazy"}),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1691067065982-c0370621-ff74-41e2-a435-b21973c64be4.png#averageHue=%23e3e4e6&clientId=ue1ff8f12-10ba-4&from=paste&height=170&id=u00ae3745&originHeight=340&originWidth=1382&originalType=binary&ratio=2&rotation=0&showTitle=false&size=116797&status=done&style=none&taskId=u563107c4-dbc5-464b-bb87-ab1c1dea427&title=&width=691",alt:"",loading:"lazy"}),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1691067101217-a60e6bd8-600c-4c68-89d0-53675a11442c.png#averageHue=%23e5e5e8&clientId=ue1ff8f12-10ba-4&from=paste&height=118&id=u1559e5c7&originHeight=236&originWidth=1370&originalType=binary&ratio=2&rotation=0&showTitle=false&size=77383&status=done&style=none&taskId=u846749a0-6dcb-429b-ad44-0d6e77dea00&title=&width=685",alt:"",loading:"lazy"})],-1),m=e("p",null,"视频里有实战演示:",-1);function f(b,u){const a=o("BiliBili");return r(),n("div",null,[h,c,s(" more "),g,m,d(a,{bvid:"BV1Dj411676U"})])}const y=t(l,[["render",f],["__file","what-is-the-difference-between-sh-and-bash.html.vue"]]);export{y as default}; diff --git a/assets/which-one-is-better-Boolean-or-boolean.html-03e8b2fa.js b/assets/which-one-is-better-Boolean-or-boolean.html-03e8b2fa.js new file mode 100644 index 00000000..515f987c --- /dev/null +++ b/assets/which-one-is-better-Boolean-or-boolean.html-03e8b2fa.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-386e94f1","path":"/java/which-one-is-better-Boolean-or-boolean.html","title":"Boolean 还是 boolean?","lang":"zh-CN","frontmatter":{"description":"Boolean 还是 boolean? 在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢? 结论 先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。 原文如下:","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/which-one-is-better-Boolean-or-boolean.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Boolean 还是 boolean?"}],["meta",{"property":"og:description","content":"Boolean 还是 boolean? 在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢? 结论 先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。 原文如下:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Boolean 还是 boolean?\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]},{"level":2,"title":"争议","slug":"争议","link":"#争议","children":[]},{"level":2,"title":"实战","slug":"实战","link":"#实战","children":[{"level":3,"title":"简单例子","slug":"简单例子","link":"#简单例子","children":[]},{"level":3,"title":"复杂例子","slug":"复杂例子","link":"#复杂例子","children":[]}]},{"level":2,"title":"附","slug":"附","link":"#附","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.5,"words":1051},"filePathRelative":"java/which-one-is-better-Boolean-or-boolean.md","localizedDate":"2023年11月22日","excerpt":"

Boolean 还是 boolean?

\\n

在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢?

\\n

结论

\\n

先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。

\\n

原文如下:
\\n\\"image.png\\"

","autoDesc":true}');export{e as data}; diff --git a/assets/which-one-is-better-Boolean-or-boolean.html-90421d51.js b/assets/which-one-is-better-Boolean-or-boolean.html-90421d51.js new file mode 100644 index 00000000..fb78bf00 --- /dev/null +++ b/assets/which-one-is-better-Boolean-or-boolean.html-90421d51.js @@ -0,0 +1,16 @@ +import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as p,c,a,b as n,d as s,f as o}from"./app-a9d55428.js";const i={},r=o('

Boolean 还是 boolean?

在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢?

结论

先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。

原文如下:
image.png

红线处翻译:总结就是,当你有得选的时候,请务必使用基本类型,而非包装类型。

那什么时候使用包装类型呢?原文如下:
image.png

红线处翻译:当你没得选、被强制要求时,才使用包装类型。如:使用泛型(使用集合类、调用参数是泛型参数的方法),以及通过反射进行方法调用(使用 invoke 方法)

争议

',9),d={href:"https://github.com/alibaba/p3c/blob/master/Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C(%E9%BB%84%E5%B1%B1%E7%89%88).pdf",target:"_blank",rel:"noopener noreferrer"},u=a("br",null,null,-1),b=a("img",{src:"https://cdn.nlark.com/yuque/0/2022/png/160590/1654076977162-f5358442-5426-4bca-936b-fcedcff0d3d3.png",alt:"image.png",loading:"lazy"},null,-1),h=o(`

但注意,本文讨论的仅仅是布尔类型,不要发散话题。 那现在就来分析一下,布尔类型有没有必要考虑 null 的情况?

我认为是没有的必要的。理由如下:

  • 布尔类型就是二进制的,代码两种情况:1或0;真或假。使用包装类型,出现第三种情况 null,不但要注意空指针异常问题,还要兼容 null 的情况——此时到底是真还是假呢?
  • 如果 null 表示的既不是真也不假,而是第三种情况——就不该定义为布尔类型,而应定义为枚举类型,因为一共有三种情况。使用 Boolean 来表示三种情况,是设计上的偷懒。

实战

我们来看一下实际代码中,滥用 Boolean 类型导致的问题。

简单例子

有如下 Controller,使用的是 boolean:

@RestController
+public class DemoController {
+  @RequestMapping("/hello")
+  public String hello(boolean bool) {
+   return String.valueOf(bool) ;
+  }
+}
+

前端传个空值,会得到报错信息:
image.png
但如果使用 Boolean,并不能检查出是非法参数:
image.png
这原本是前端传参错误,但却无法即使发现。

你可能会问,为什么不先判断 Boolean 变量是否为 null 呢?因为别人在声明 Boolean 变量时,设置了默认值为 false,谁能想到前端会传个 null?

这就是 Boolean 的问题:“千防万防,家贼难防”!

复杂例子

下面是前端传给 Controller 的参数:

public class TableQuery {
+    private Boolean withTable2Api;
+    private Boolean forkable;
+}	
+

需要注意的是,有至少两个 Controller 会接受到了这个参数,并且根据业务的不同,它们对参数的处理是不同的:

  • 有的会判断如果 withTable2Api 为 null,就设置为 true
  • 有的则完全由前端传值,也即 forkable、withTable2Api 有可能为 null

来看一下,Service 里的相应代码:
image.png
这里有什么问题呢?可以看到,第二个 if 使用 !Boolean._TRUE_去判断很别扭,然而,却不能改写成:

// 错误!因为没有兼容 null 的情况
+if (Boolean.FALSE.equals(query.getWithTable2Api())) {
+   
+}
+

这就是布尔值使用 Boolean 类型在实战中最大的问题——你不能任意地进行真或假的判断,而必须兼容上下文中隐式对 null 赋予的含义。

而多人协作过程中,外部调用是很难控制的,因此,此时使用 Boolean,只增加了无谓的编码负担。

`,21),k={href:"https://www.yuque.com/attachments/yuque/0/2022/pdf/160590/1654077188552-25d2e37f-f34c-46db-817e-569163265605.pdf?_lake_card=%7B%22src%22%3A%22https%3A%2F%2Fwww.yuque.com%2Fattachments%2Fyuque%2F0%2F2022%2Fpdf%2F160590%2F1654077188552-25d2e37f-f34c-46db-817e-569163265605.pdf%22%2C%22name%22%3A%22Joshua%20Bloch%20-%20Effective%20Java%20(3rd)%20-%202018.pdf%22%2C%22size%22%3A2294786%2C%22ext%22%3A%22pdf%22%2C%22source%22%3A%22%22%2C%22status%22%3A%22done%22%2C%22download%22%3Atrue%2C%22type%22%3A%22application%2Fpdf%22%2C%22mode%22%3A%22title%22%2C%22taskId%22%3A%22u4da28c90-dfee-481c-b1b9-7ab6c120cdd%22%2C%22taskType%22%3A%22upload%22%2C%22__spacing%22%3A%22both%22%2C%22id%22%3A%22ud6462faf%22%2C%22margin%22%3A%7B%22top%22%3Atrue%2C%22bottom%22%3Atrue%7D%2C%22card%22%3A%22file%22%7D",target:"_blank",rel:"noopener noreferrer"};function m(f,v){const e=t("ExternalLinkIcon");return p(),c("div",null,[r,a("p",null,[n("阿里的"),a("a",d,[n("《Java开发手册》"),s(e)]),n("有提到,类属性强制使用包装类型。"),u,b,n("文中举的例子还是有道理的:有时需要 null 来表示额外的信息。")]),h,a("p",null,[a("a",k,[n("Joshua Bloch - Effective Java (3rd) - 2018.pdf"),s(e)])])])}const B=l(i,[["render",m],["__file","which-one-is-better-Boolean-or-boolean.html.vue"]]);export{B as default}; diff --git a/assets/which-one-is-better-forEach-or-map.html-16d69e48.js b/assets/which-one-is-better-forEach-or-map.html-16d69e48.js new file mode 100644 index 00000000..1bc69281 --- /dev/null +++ b/assets/which-one-is-better-forEach-or-map.html-16d69e48.js @@ -0,0 +1 @@ +const a=JSON.parse('{"key":"v-0d6828c2","path":"/java/which-one-is-better-forEach-or-map.html","title":"forEach 还是 map?","lang":"zh-CN","frontmatter":{"description":"forEach 还是 map? 背景 遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有: List<Type> result = new ArrayList<>(); list.forEach(src -> { Type target = BeanUtils.copyProperties(src, target); //省略代码 result.add(target); });","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/which-one-is-better-forEach-or-map.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"forEach 还是 map?"}],["meta",{"property":"og:description","content":"forEach 还是 map? 背景 遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有: List<Type> result = new ArrayList<>(); list.forEach(src -> { Type target = BeanUtils.copyProperties(src, target); //省略代码 result.add(target); });"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"forEach 还是 map?\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]},{"level":2,"title":"解析","slug":"解析","link":"#解析","children":[]},{"level":2,"title":"实战","slug":"实战","link":"#实战","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.64,"words":1091},"filePathRelative":"java/which-one-is-better-forEach-or-map.md","localizedDate":"2023年11月22日","excerpt":"

forEach 还是 map?

\\n

背景

\\n

遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有:

\\n
List<Type> result = new ArrayList<>();\\nlist.forEach(src -> {\\n    Type target = BeanUtils.copyProperties(src, target);\\n    //省略代码\\n    result.add(target);\\n});\\n
","autoDesc":true}');export{a as data}; diff --git a/assets/which-one-is-better-forEach-or-map.html-7837b1cd.js b/assets/which-one-is-better-forEach-or-map.html-7837b1cd.js new file mode 100644 index 00000000..138a6b53 --- /dev/null +++ b/assets/which-one-is-better-forEach-or-map.html-7837b1cd.js @@ -0,0 +1,71 @@ +import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{o as s,c as a,f as p}from"./app-a9d55428.js";const t={},e=p(`

forEach 还是 map?

背景

遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有:

List<Type> result = new ArrayList<>();
+list.forEach(src -> {
+    Type target = BeanUtils.copyProperties(src, target);
+    //省略代码
+    result.add(target);
+});
+
List<Type> result = list.map(src -> {
+    Type target = BeanUtils.copyProperties(src, target);
+    //省略代码
+   return target;
+});
+

两种方式看起来没多大差别啊,到底用哪种呢?

结论

先说结论:根据《Effective Java》(第三版),forEach 只用于消费数据的场景,并不应该用于计算、累加,故上述代码应该使用 map。

原文如下:
image.png

红字处翻译:forEach 仅适用于输出 stream 里的计算结果,并不适合执行计算。

解析

为更好地理解上述结论,需要先理解以下内涵:
image.png
Stream 的引入,不仅带来新的语法,也带来了函数式编程的思维。

这里最重要的一点就是:编写纯函数(pure function),不造成副作用(side-effect)。

纯函数可以用数学中的函数映射来理解:y = f(x)

  • 给定 x,能唯一确定 y
  • 无论函数调用几次、在何处调用,上述结果都不会变化

无副作用意思是:调用函数后,不会对函数作用域以外的变量造成影响。而纯函数,一定是无副作用的。

前文使用 forEach 的代码,其实是造成了副作用的:

List<Type> result = new ArrayList<>();
+list.forEach(src -> {
+    Type target = BeanUtils.copyProperties(src, target);
+    result.add(target); //side effect
+});
+

很简单的一个识别方法:再调用一次 forEach,result 的结果还是期望的结果吗?显然不是。

但如果 map 方法呢?再调用一次,结果不变!

List<Type> result = list.map(src -> {
+    Type target = BeanUtils.copyProperties(src, target);
+   return target; // side effect free
+});
+

类似的,修改函数入参,也不是纯函数:

List<String> ids = new ArrayList<>();
+
+collectIds(data, ids); // 在这里填充 ids!
+
+int size = ids.size(); 
+

上面的 collectIds函数是令人讨厌的——写代码的人懒得写函数返回时,直接修改函数入参,给后面维护的人留下隐患。

当然,如果一定要用纯函数来看待问题,未免过于理想化,因为有时要执行这样的代码:

list.forEach(v -> {
+    myService.save(v);
+});
+

虽然上述代码并没影响到函数作用域以外的代码变量,但 myService 会把数据持久化,站在整个应用的角度讲,仍然造成了副作用。

但上述代码可以接受的。因此,建议记住 forEach 只用于消费数据,不用于计算及返回,就不容易混淆。

实战

当然,上述的讨论还是比较偏理论的,我们来看一下实际项目中,滥用 forEach 可能导致可读性较差的问题。

代码一开始,是简单清晰的:

// 一个只有20行的函数
+void myFunction(List<Node> nodes, List<Edge> edges) {
+    Type1 var1;
+    Type2 var2;
+    
+    nodes.forEach(node -> {
+        // 修改 var1
+    })
+      
+    edges.forEach(edge -> {
+        // 修改 var2
+    })
+}
+

然而,业务会变化,逻辑会复杂,代码也要修改。而上述在 forEach 中修改变量的行为,罪恶的根源在于,它在向后来修改代码的人发出邀请:新增的逻辑,写在这个 forEach 里面就好了!

当仅在 forEach 添加代码就能完成任务的时候,很难有人能抵抗这种诱惑,于是就会变成:

// 一个超过50行的函数
+void myFunction(List<Node> nodes, List<Edge> edges) {
+    Type1 var1;
+    Type2 var2;
+    Type3 var3;
+    Type4 var4;
+    Type5 var5;
+    
+    nodes.forEach(node -> {
+        // 里面有多个 if-else
+        // 修改了 var1, var2
+        // 有可能修改 var5
+    })
+      
+    edges.forEach(edge -> {
+        // 里面有多个 if-else
+        // 修改了 var3, var4
+        // 有可能修改 var5
+    })
+}
+

写代码一时爽,维护火葬场。上面的代码,将是维护的噩梦!

并且,如果维护者只想知道函数的整体逻辑,由于变量穿插、隐藏在 forEach 内部,维护者不得不在各种 if-else 里面追踪变量,很容易就陷入不必要的细节中。

如果换个方式来写呢?

void myFunction(List<Node> nodes, List<Edge> edges) {   
+    // 为代码简洁,省略了 collect(Collectors.toList())
+    Type1 var1 = nodes.map(v -> getVar1(v));
+    Type2 var2 = nodes.map(v -> getVar2(v));
+    Type3 var3 = edges.map(v -> getVar3(v));
+    Type4 var4 = edges.map(v -> getVar4(v));
+    Type5 var5 = Stream.of(nodes, edges).filter(v -> filterVar5(v));
+    
+}
+

效果有大大的不同!

现在想追查哪个变量,简单轻松好多啦!

`,41),c=[e];function o(l,i){return s(),a("div",null,c)}const k=n(t,[["render",o],["__file","which-one-is-better-forEach-or-map.html.vue"]]);export{k as default}; diff --git a/assets/why-i-prefer-fastjson-instead-of-jackson.html-f1819a15.js b/assets/why-i-prefer-fastjson-instead-of-jackson.html-67cee556.js similarity index 92% rename from assets/why-i-prefer-fastjson-instead-of-jackson.html-f1819a15.js rename to assets/why-i-prefer-fastjson-instead-of-jackson.html-67cee556.js index 15b46053..32bd40c8 100644 --- a/assets/why-i-prefer-fastjson-instead-of-jackson.html-f1819a15.js +++ b/assets/why-i-prefer-fastjson-instead-of-jackson.html-67cee556.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-1aafac08","path":"/java/why-i-prefer-fastjson-instead-of-jackson.html","title":"Jackson 经典异常 UnrecognizedPropertyException","lang":"zh-CN","frontmatter":{"date":"2023-08-21T00:00:00.000Z","tag":["Java","Daily","Video"],"description":"Jackson 经典异常 UnrecognizedPropertyException 原因是 json 包含的字段,多于 Java 实体类定义的字段。 解决方法很简单: new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) 或者为相关实体添加注解: @JsonIgnoreProperties(ignoreUnknown = true) public class ObjectParseFromJsonString { }","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/why-i-prefer-fastjson-instead-of-jackson.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Jackson 经典异常 UnrecognizedPropertyException"}],["meta",{"property":"og:description","content":"Jackson 经典异常 UnrecognizedPropertyException 原因是 json 包含的字段,多于 Java 实体类定义的字段。 解决方法很简单: new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) 或者为相关实体添加注解: @JsonIgnoreProperties(ignoreUnknown = true) public class ObjectParseFromJsonString { }"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Jackson 经典异常 UnrecognizedPropertyException\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-21T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.36,"words":109},"filePathRelative":"java/why-i-prefer-fastjson-instead-of-jackson.md","localizedDate":"2023年8月21日","excerpt":"

Jackson 经典异常 UnrecognizedPropertyException

\\n

原因是 json 包含的字段,多于 Java 实体类定义的字段。

\\n

解决方法很简单:

\\n
new ObjectMapper()\\n  .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)\\n

或者为相关实体添加注解:

\\n
@JsonIgnoreProperties(ignoreUnknown = true)\\npublic class ObjectParseFromJsonString {  }\\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-1aafac08","path":"/java/why-i-prefer-fastjson-instead-of-jackson.html","title":"Jackson 经典异常 UnrecognizedPropertyException","lang":"zh-CN","frontmatter":{"date":"2023-08-21T00:00:00.000Z","tag":["Java","Daily","Video"],"description":"Jackson 经典异常 UnrecognizedPropertyException 原因是 json 包含的字段,多于 Java 实体类定义的字段。 解决方法很简单: new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) 或者为相关实体添加注解: @JsonIgnoreProperties(ignoreUnknown = true) public class ObjectParseFromJsonString { }","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/why-i-prefer-fastjson-instead-of-jackson.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Jackson 经典异常 UnrecognizedPropertyException"}],["meta",{"property":"og:description","content":"Jackson 经典异常 UnrecognizedPropertyException 原因是 json 包含的字段,多于 Java 实体类定义的字段。 解决方法很简单: new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) 或者为相关实体添加注解: @JsonIgnoreProperties(ignoreUnknown = true) public class ObjectParseFromJsonString { }"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Jackson 经典异常 UnrecognizedPropertyException\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-21T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.36,"words":109},"filePathRelative":"java/why-i-prefer-fastjson-instead-of-jackson.md","localizedDate":"2023年8月21日","excerpt":"

Jackson 经典异常 UnrecognizedPropertyException

\\n

原因是 json 包含的字段,多于 Java 实体类定义的字段。

\\n

解决方法很简单:

\\n
new ObjectMapper()\\n  .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)\\n

或者为相关实体添加注解:

\\n
@JsonIgnoreProperties(ignoreUnknown = true)\\npublic class ObjectParseFromJsonString {  }\\n
","autoDesc":true}');export{e as data}; diff --git a/assets/why-i-prefer-fastjson-instead-of-jackson.html-3961785e.js b/assets/why-i-prefer-fastjson-instead-of-jackson.html-ef0d6e0e.js similarity index 97% rename from assets/why-i-prefer-fastjson-instead-of-jackson.html-3961785e.js rename to assets/why-i-prefer-fastjson-instead-of-jackson.html-ef0d6e0e.js index 02e1da44..e113ccad 100644 --- a/assets/why-i-prefer-fastjson-instead-of-jackson.html-3961785e.js +++ b/assets/why-i-prefer-fastjson-instead-of-jackson.html-ef0d6e0e.js @@ -1,4 +1,4 @@ -import{_ as c}from"./plugin-vue_export-helper-c27b6911.js";import{r as s,o as p,c as i,e as r,a,b as n,d as e,f as l}from"./app-b649ee34.js";const d={},u=l(`

Jackson 经典异常 UnrecognizedPropertyException

原因是 json 包含的字段,多于 Java 实体类定义的字段。

解决方法很简单:

new ObjectMapper()
+import{_ as c}from"./plugin-vue_export-helper-c27b6911.js";import{r as s,o as p,c as i,e as r,a,b as n,d as e,f as l}from"./app-a9d55428.js";const d={},u=l(`

Jackson 经典异常 UnrecognizedPropertyException

原因是 json 包含的字段,多于 Java 实体类定义的字段。

解决方法很简单:

new ObjectMapper()
   .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
 

或者为相关实体添加注解:

@JsonIgnoreProperties(ignoreUnknown = true)
 public class ObjectParseFromJsonString {  }
diff --git a/assets/why-is-it-so-hard-to-upgrade-dependencies.html-08962984.js b/assets/why-is-it-so-hard-to-upgrade-dependencies.html-8df8757e.js
similarity index 98%
rename from assets/why-is-it-so-hard-to-upgrade-dependencies.html-08962984.js
rename to assets/why-is-it-so-hard-to-upgrade-dependencies.html-8df8757e.js
index 75230c8c..08bbef51 100644
--- a/assets/why-is-it-so-hard-to-upgrade-dependencies.html-08962984.js
+++ b/assets/why-is-it-so-hard-to-upgrade-dependencies.html-8df8757e.js
@@ -1 +1 @@
-import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as t,c as n,e as d,d as c,a as e,b as a,f as s}from"./app-b649ee34.js";const h={},l=e("h1",{id:"升个jar版本-怎么这么难",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#升个jar版本-怎么这么难","aria-hidden":"true"},"#"),a(" 升个jar版本,怎么这么难?")],-1),p=e("h2",{id:"前言",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),a(" 前言")],-1),f=e("p",null,"无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。",-1),_=e("p",null,"通常来说,对 Web 应用而言,前端是消费者,后端是提供者。",-1),m=e("p",null,"今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?",-1),u=s('

接口调用示意图

已知信息

  1. common.jar 的版本是 1.0.0-SNAPSHOT,实际上 common.jar 的最新稳定版已经是 5.x 了。比较了下版本内容,发现二者是不兼容的。
  2. 公司有多个业务系统依赖 infra-service,但它们隶属于别的团队

问题

infra-service 的某个接口,现在要修改,多返回一个字段,但因为之前的代码健壮性不够,新接口 common.jar 处理会报错。

于是,想通过让使用方升级 jar 来解决,此方案可行吗?

答案可能出乎意料:不行!

因为:jar 升级版本的动作不在自己的掌控范围内。

说起来很简单:叫他们升一下就行。
但问题是:

  1. 他们是谁?
  2. 你说升就升?

问题本身在技术层面很简单,但在现实的执行层面,有着超出技术范畴的难点。这便是服务提供者,需要面对的。而这是作为服务的消费者,容易忽视的一点。

复盘

那么,在技术层面,是否能做得更好,避免重蹈覆辙呢?其实是可以的。

有两种思路:

  1. 只对外发布稳定版,要么强制自己的接口一直向后兼容;要么一旦有接口不兼容,在保留旧接口的情况下,发布新接口、新 jar。
  2. 利用好 SNAPSHOT 版本 jar 会不断覆盖的特性,同时对外发布 1.0.0-SNAPSHOT 版本,以及每次迭代的稳定版。也即,通过使得 1.0.0-SNAPSHOT 永远与最新的稳定版本相同,让使用者及时升级。
',16);function b(g,x){const i=o("BiliBili");return t(),n("div",null,[l,p,f,_,m,d(" more "),u,c(i,{bvid:"BV18G411d7gR"})])}const B=r(h,[["render",b],["__file","why-is-it-so-hard-to-upgrade-dependencies.html.vue"]]);export{B as default}; +import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as t,c as n,e as d,d as c,a as e,b as a,f as s}from"./app-a9d55428.js";const h={},l=e("h1",{id:"升个jar版本-怎么这么难",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#升个jar版本-怎么这么难","aria-hidden":"true"},"#"),a(" 升个jar版本,怎么这么难?")],-1),p=e("h2",{id:"前言",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),a(" 前言")],-1),f=e("p",null,"无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。",-1),_=e("p",null,"通常来说,对 Web 应用而言,前端是消费者,后端是提供者。",-1),m=e("p",null,"今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?",-1),u=s('

接口调用示意图

已知信息

  1. common.jar 的版本是 1.0.0-SNAPSHOT,实际上 common.jar 的最新稳定版已经是 5.x 了。比较了下版本内容,发现二者是不兼容的。
  2. 公司有多个业务系统依赖 infra-service,但它们隶属于别的团队

问题

infra-service 的某个接口,现在要修改,多返回一个字段,但因为之前的代码健壮性不够,新接口 common.jar 处理会报错。

于是,想通过让使用方升级 jar 来解决,此方案可行吗?

答案可能出乎意料:不行!

因为:jar 升级版本的动作不在自己的掌控范围内。

说起来很简单:叫他们升一下就行。
但问题是:

  1. 他们是谁?
  2. 你说升就升?

问题本身在技术层面很简单,但在现实的执行层面,有着超出技术范畴的难点。这便是服务提供者,需要面对的。而这是作为服务的消费者,容易忽视的一点。

复盘

那么,在技术层面,是否能做得更好,避免重蹈覆辙呢?其实是可以的。

有两种思路:

  1. 只对外发布稳定版,要么强制自己的接口一直向后兼容;要么一旦有接口不兼容,在保留旧接口的情况下,发布新接口、新 jar。
  2. 利用好 SNAPSHOT 版本 jar 会不断覆盖的特性,同时对外发布 1.0.0-SNAPSHOT 版本,以及每次迭代的稳定版。也即,通过使得 1.0.0-SNAPSHOT 永远与最新的稳定版本相同,让使用者及时升级。
',16);function b(g,x){const i=o("BiliBili");return t(),n("div",null,[l,p,f,_,m,d(" more "),u,c(i,{bvid:"BV18G411d7gR"})])}const B=r(h,[["render",b],["__file","why-is-it-so-hard-to-upgrade-dependencies.html.vue"]]);export{B as default}; diff --git a/assets/why-is-it-so-hard-to-upgrade-dependencies.html-f4295ff8.js b/assets/why-is-it-so-hard-to-upgrade-dependencies.html-996256d6.js similarity index 88% rename from assets/why-is-it-so-hard-to-upgrade-dependencies.html-f4295ff8.js rename to assets/why-is-it-so-hard-to-upgrade-dependencies.html-996256d6.js index af9efb83..c553ba31 100644 --- a/assets/why-is-it-so-hard-to-upgrade-dependencies.html-f4295ff8.js +++ b/assets/why-is-it-so-hard-to-upgrade-dependencies.html-996256d6.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-ca672354","path":"/java/why-is-it-so-hard-to-upgrade-dependencies.html","title":"升个jar版本,怎么这么难?","lang":"zh-CN","frontmatter":{"date":"2023-08-26T00:00:00.000Z","tag":["Java","Daily","Video"],"description":"升个jar版本,怎么这么难? 前言 无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。 通常来说,对 Web 应用而言,前端是消费者,后端是提供者。 今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/why-is-it-so-hard-to-upgrade-dependencies.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"升个jar版本,怎么这么难?"}],["meta",{"property":"og:description","content":"升个jar版本,怎么这么难? 前言 无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。 通常来说,对 Web 应用而言,前端是消费者,后端是提供者。 今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-26T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"升个jar版本,怎么这么难?\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-26T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"接口调用示意图","slug":"接口调用示意图","link":"#接口调用示意图","children":[]},{"level":2,"title":"已知信息","slug":"已知信息","link":"#已知信息","children":[]},{"level":2,"title":"问题","slug":"问题","link":"#问题","children":[]},{"level":2,"title":"复盘","slug":"复盘","link":"#复盘","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.8,"words":540},"filePathRelative":"java/why-is-it-so-hard-to-upgrade-dependencies.md","localizedDate":"2023年8月26日","excerpt":"

升个jar版本,怎么这么难?

\\n

前言

\\n

无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。

\\n

通常来说,对 Web 应用而言,前端是消费者,后端是提供者。

\\n

今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-ca672354","path":"/java/why-is-it-so-hard-to-upgrade-dependencies.html","title":"升个jar版本,怎么这么难?","lang":"zh-CN","frontmatter":{"date":"2023-08-26T00:00:00.000Z","tag":["Java","Daily","Video"],"description":"升个jar版本,怎么这么难? 前言 无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。 通常来说,对 Web 应用而言,前端是消费者,后端是提供者。 今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/why-is-it-so-hard-to-upgrade-dependencies.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"升个jar版本,怎么这么难?"}],["meta",{"property":"og:description","content":"升个jar版本,怎么这么难? 前言 无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。 通常来说,对 Web 应用而言,前端是消费者,后端是提供者。 今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-26T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"升个jar版本,怎么这么难?\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-26T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"接口调用示意图","slug":"接口调用示意图","link":"#接口调用示意图","children":[]},{"level":2,"title":"已知信息","slug":"已知信息","link":"#已知信息","children":[]},{"level":2,"title":"问题","slug":"问题","link":"#问题","children":[]},{"level":2,"title":"复盘","slug":"复盘","link":"#复盘","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.8,"words":540},"filePathRelative":"java/why-is-it-so-hard-to-upgrade-dependencies.md","localizedDate":"2023年8月26日","excerpt":"

升个jar版本,怎么这么难?

\\n

前言

\\n

无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。

\\n

通常来说,对 Web 应用而言,前端是消费者,后端是提供者。

\\n

今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/you-dont-need-to-add-tenant_id-to-every-table.html-d1b8df99.js b/assets/you-dont-need-to-add-tenant_id-to-every-table.html-36585552.js similarity index 98% rename from assets/you-dont-need-to-add-tenant_id-to-every-table.html-d1b8df99.js rename to assets/you-dont-need-to-add-tenant_id-to-every-table.html-36585552.js index dfd2fb43..709f5a18 100644 --- a/assets/you-dont-need-to-add-tenant_id-to-every-table.html-d1b8df99.js +++ b/assets/you-dont-need-to-add-tenant_id-to-every-table.html-36585552.js @@ -1 +1 @@ -import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{o as n,c as d,e as i,a as e,b as t,f as r}from"./app-b649ee34.js";const c={},o=e("h1",{id:"技术点评-别每张表都加tenant-id",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#技术点评-别每张表都加tenant-id","aria-hidden":"true"},"#"),t(" 技术点评:别每张表都加tenant_id")],-1),s=e("h2",{id:"前言",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),t(" 前言")],-1),_=e("p",null,"系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。",-1),h=r('

背景

在维护旧系统时,踩了各种坑,终于忍不住在群里吐槽了下,于是有以下对话。



既然群友有疑问,我索性就整理了一下,把前因后果清楚。

正文

1.首先,租户数据隔离级别应该如何,没有唯一标准,评价只有是否合适。因此,逻辑隔离、物理隔离,都不是吐槽点。

2.原来的设计是,采取逻辑隔离方案,具体做法是,给每一张表都加上了tenant_id字段。问题就在于,有必要每一张表加吗?系统的权限设计是:先有租户,再有应用,用户、角色、资源、权限设置等内容都挂在应用下,所以,应用下的内容,关联 app_id 就行了,根本不需要tenant_id。

3.冗余多一个字段,会出现什么问题呢?先不提查询性能、存储空间等细节,就说很实际的场景:
3.1 每张表都加 tenant_id,几乎每条 sql 都要加 where tenant_id = ? ,那程序员会怎么做?首先想到的是用框架自动注入 sql
3.2 在后台管理端,有一个超级管理员,能够查询出所有租户的内容,也就是说,此时查询不能带 where tenant_id = ?,也即不能让框架注入 sql

要同时兼容上述逻辑,程序员又会怎么做呢?于是就引入了万恶的全局变量,类似于 injectSql = true,就添加 tenant_id 作为过滤条件。但默认是不是要 injectSql = true 呢?每个项目代码又不一样,你不运行,你都不知道。

更恶心的是,需求变化后,是否需要带上 tenant_id 的逻辑与原来不一致时,你得在某行代码执行前,手动设置 inejctSql 的值,在该行代码之后,再手动复原——因为如果不复原,作为全局变量,会影响到后面的代码!

md,这时候后你才会知道,还不如老老实实地设置 tenant_id,显示地设置,好过这种隐蔽的依赖。

4.但这还不是最难搞的。因为上述的是代码问题,真正难搞的是数据问题。考虑一种场景:超级管理员在后台管理某租户的应用,手动为租户添加数据,请问,新增的数据 tenant_id 的值是什么,某租户的 id,还是超级管理员的id?按逻辑来说,应该是某租户的 tenant_id。但问题在于,由于理解不同,或由于疏忽让框架自动注入了 tenant_id,导致上述场景,有些数据的 tenant_id 是超级管理员的id。而又因为超级管理员进行查询时,是不带 tenant_id 作为过滤条件的,因此即使 tenant_id 的值设置错误,依然在界面上能显示,使得这个问题一直存在着,旧数据一直被保留并使用。

5.现在,有需求要导出某个租户下的数据,结果发现 tenant_id 乱七八糟,你难不难受?

6.那么,梳理完逻辑链条,我认为,虽然某些程序员的在实现上犯了低级错误,但不是主要原因,罪魁祸首应该是设计上的懒惰。设计精细一点,明确好 tenant_id 到哪张表为止,也就没有后面的 sql 注入、数据错误那么多事了。所以,我才说,给租户隔离等于给每张表加 tenant_id 的设计很傻逼!

',12);function l(p,m){return n(),d("div",null,[o,s,_,i(" more "),h])}const f=a(c,[["render",l],["__file","you-dont-need-to-add-tenant_id-to-every-table.html.vue"]]);export{f as default}; +import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{o as n,c as d,e as i,a as e,b as t,f as r}from"./app-a9d55428.js";const c={},o=e("h1",{id:"技术点评-别每张表都加tenant-id",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#技术点评-别每张表都加tenant-id","aria-hidden":"true"},"#"),t(" 技术点评:别每张表都加tenant_id")],-1),s=e("h2",{id:"前言",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),t(" 前言")],-1),_=e("p",null,"系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。",-1),h=r('

背景

在维护旧系统时,踩了各种坑,终于忍不住在群里吐槽了下,于是有以下对话。



既然群友有疑问,我索性就整理了一下,把前因后果清楚。

正文

1.首先,租户数据隔离级别应该如何,没有唯一标准,评价只有是否合适。因此,逻辑隔离、物理隔离,都不是吐槽点。

2.原来的设计是,采取逻辑隔离方案,具体做法是,给每一张表都加上了tenant_id字段。问题就在于,有必要每一张表加吗?系统的权限设计是:先有租户,再有应用,用户、角色、资源、权限设置等内容都挂在应用下,所以,应用下的内容,关联 app_id 就行了,根本不需要tenant_id。

3.冗余多一个字段,会出现什么问题呢?先不提查询性能、存储空间等细节,就说很实际的场景:
3.1 每张表都加 tenant_id,几乎每条 sql 都要加 where tenant_id = ? ,那程序员会怎么做?首先想到的是用框架自动注入 sql
3.2 在后台管理端,有一个超级管理员,能够查询出所有租户的内容,也就是说,此时查询不能带 where tenant_id = ?,也即不能让框架注入 sql

要同时兼容上述逻辑,程序员又会怎么做呢?于是就引入了万恶的全局变量,类似于 injectSql = true,就添加 tenant_id 作为过滤条件。但默认是不是要 injectSql = true 呢?每个项目代码又不一样,你不运行,你都不知道。

更恶心的是,需求变化后,是否需要带上 tenant_id 的逻辑与原来不一致时,你得在某行代码执行前,手动设置 inejctSql 的值,在该行代码之后,再手动复原——因为如果不复原,作为全局变量,会影响到后面的代码!

md,这时候后你才会知道,还不如老老实实地设置 tenant_id,显示地设置,好过这种隐蔽的依赖。

4.但这还不是最难搞的。因为上述的是代码问题,真正难搞的是数据问题。考虑一种场景:超级管理员在后台管理某租户的应用,手动为租户添加数据,请问,新增的数据 tenant_id 的值是什么,某租户的 id,还是超级管理员的id?按逻辑来说,应该是某租户的 tenant_id。但问题在于,由于理解不同,或由于疏忽让框架自动注入了 tenant_id,导致上述场景,有些数据的 tenant_id 是超级管理员的id。而又因为超级管理员进行查询时,是不带 tenant_id 作为过滤条件的,因此即使 tenant_id 的值设置错误,依然在界面上能显示,使得这个问题一直存在着,旧数据一直被保留并使用。

5.现在,有需求要导出某个租户下的数据,结果发现 tenant_id 乱七八糟,你难不难受?

6.那么,梳理完逻辑链条,我认为,虽然某些程序员的在实现上犯了低级错误,但不是主要原因,罪魁祸首应该是设计上的懒惰。设计精细一点,明确好 tenant_id 到哪张表为止,也就没有后面的 sql 注入、数据错误那么多事了。所以,我才说,给租户隔离等于给每张表加 tenant_id 的设计很傻逼!

',12);function l(p,m){return n(),d("div",null,[o,s,_,i(" more "),h])}const f=a(c,[["render",l],["__file","you-dont-need-to-add-tenant_id-to-every-table.html.vue"]]);export{f as default}; diff --git a/assets/you-dont-need-to-add-tenant_id-to-every-table.html-fadcd1b3.js b/assets/you-dont-need-to-add-tenant_id-to-every-table.html-d78e0fcf.js similarity index 86% rename from assets/you-dont-need-to-add-tenant_id-to-every-table.html-fadcd1b3.js rename to assets/you-dont-need-to-add-tenant_id-to-every-table.html-d78e0fcf.js index af86c350..ee5a2deb 100644 --- a/assets/you-dont-need-to-add-tenant_id-to-every-table.html-fadcd1b3.js +++ b/assets/you-dont-need-to-add-tenant_id-to-every-table.html-d78e0fcf.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-bd2b5fe8","path":"/daily/you-dont-need-to-add-tenant_id-to-every-table.html","title":"技术点评:别每张表都加tenant_id","lang":"zh-CN","frontmatter":{"date":"2023-09-18T00:00:00.000Z","tag":["Design","Daily"],"description":"技术点评:别每张表都加tenant_id 前言 系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/you-dont-need-to-add-tenant_id-to-every-table.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"技术点评:别每张表都加tenant_id"}],["meta",{"property":"og:description","content":"技术点评:别每张表都加tenant_id 前言 系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-10-26T05:50:24.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Design"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-18T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-10-26T05:50:24.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"技术点评:别每张表都加tenant_id\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-18T00:00:00.000Z\\",\\"dateModified\\":\\"2023-10-26T05:50:24.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"正文","slug":"正文","link":"#正文","children":[]}],"git":{"createdTime":1698299424000,"updatedTime":1698299424000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.26,"words":977},"filePathRelative":"daily/you-dont-need-to-add-tenant_id-to-every-table.md","localizedDate":"2023年9月18日","excerpt":"

技术点评:别每张表都加tenant_id

\\n

前言

\\n

系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-bd2b5fe8","path":"/daily/you-dont-need-to-add-tenant_id-to-every-table.html","title":"技术点评:别每张表都加tenant_id","lang":"zh-CN","frontmatter":{"date":"2023-09-18T00:00:00.000Z","tag":["Design","Daily"],"description":"技术点评:别每张表都加tenant_id 前言 系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/you-dont-need-to-add-tenant_id-to-every-table.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"技术点评:别每张表都加tenant_id"}],["meta",{"property":"og:description","content":"技术点评:别每张表都加tenant_id 前言 系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2023-11-22T09:59:56.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Design"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-18T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2023-11-22T09:59:56.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"技术点评:别每张表都加tenant_id\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-18T00:00:00.000Z\\",\\"dateModified\\":\\"2023-11-22T09:59:56.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"正文","slug":"正文","link":"#正文","children":[]}],"git":{"createdTime":1700647196000,"updatedTime":1700647196000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.26,"words":977},"filePathRelative":"daily/you-dont-need-to-add-tenant_id-to-every-table.md","localizedDate":"2023年9月18日","excerpt":"

技术点评:别每张表都加tenant_id

\\n

前言

\\n

系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。

\\n","autoDesc":true}');export{e as data}; diff --git a/category/index.html b/category/index.html index bc926288..83df8ad2 100644 --- a/category/index.html +++ b/category/index.html @@ -34,11 +34,11 @@ } - + - - + + diff --git a/daily/a-vuepress2-entertaining-video.html b/daily/a-vuepress2-entertaining-video.html index c03179e7..4a553ba3 100644 --- a/daily/a-vuepress2-entertaining-video.html +++ b/daily/a-vuepress2-entertaining-video.html @@ -5,7 +5,7 @@ - VuePress2 娱乐视频 | levy @@ -34,10 +34,10 @@ } - + - - + + diff --git a/daily/a-warning-from-navicat.html b/daily/a-warning-from-navicat.html index 44a64f36..d9cc634b 100644 --- a/daily/a-warning-from-navicat.html +++ b/daily/a-warning-from-navicat.html @@ -5,7 +5,7 @@ - 来自Navicat的侵权警告 | levy @@ -34,10 +34,10 @@ } - + - - + + diff --git a/daily/about-arm-things-you-need-to-know.html b/daily/about-arm-things-you-need-to-know.html index 1bc8fa7d..45751441 100644 --- a/daily/about-arm-things-you-need-to-know.html +++ b/daily/about-arm-things-you-need-to-know.html @@ -5,7 +5,7 @@ - 关于 Arm 你需要了解的三件事 | levy @@ -34,10 +34,10 @@ } - + - - + + diff --git a/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html b/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html index 5c0919d8..4e00e3cd 100644 --- a/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html +++ b/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html @@ -5,7 +5,7 @@ - Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci? | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

DailyVideoMySQL

Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

Background

Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci?

CREATE TABLE `my_table` (
+    
跳至主要內容

Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

DailyVideoMySQL

Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

Background

Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci?

CREATE TABLE `my_table` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   PRIMARY KEY (`id`) USING BTREE
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -48,6 +48,6 @@
 and (utf8mb4_general_ci,IMPLICIT) for operation '='
 

and here's the solution, using the keyword COLLATE:

SET @target_tenant_id := 'your_value' COLLATE utf8mb4_unicode_ci;
 
上次编辑于:
贡献者: levy
- + diff --git a/daily/claude-ai-in-action-extract-info-from-html.html b/daily/claude-ai-in-action-extract-info-from-html.html index 3994f2bd..53503c53 100644 --- a/daily/claude-ai-in-action-extract-info-from-html.html +++ b/daily/claude-ai-in-action-extract-info-from-html.html @@ -5,7 +5,7 @@ - Claude AI应用案例,从HTML中抽取文本 | levy @@ -34,10 +34,10 @@ } - + - - + + diff --git a/daily/copy-code-may-not-be-guilty.html b/daily/copy-code-may-not-be-guilty.html index e6fc524e..f8948531 100644 --- a/daily/copy-code-may-not-be-guilty.html +++ b/daily/copy-code-may-not-be-guilty.html @@ -5,7 +5,7 @@ - 复制代码也许不是罪 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

复制代码也许不是罪

Daily

复制代码也许不是罪

前言

熟悉我的人都知道,我对代码是有追求的。

正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。

我早期认为:复制代码就是菜。

后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

正文

我总结了一下,主要有以下原因:
1.深刻地理解了源码与测试用例之间的关系。只相信测试用例,而不是相信源码。如果你只改源码而不是补充相应的测试代码,那所谓的有追求是盲目、片面的。

2.对具体的编码实现已经缺乏兴致了。尤其是有AI的情况下,真的是有手就会,何必要我来写呢?我负责设计,再进行 Code Review 不是更好吗?


3.要考虑时间成本。如果别人已经做了一大半,我何必重头开始?

4.提升对质量的认知。聚集于产出质量,而不仅仅是代码质量,那么你就会发现,要做的事还挺多,再考虑时间成本,需要做一个权衡取舍。

上次编辑于:
贡献者: levy
- +
跳至主要內容

复制代码也许不是罪

Daily

复制代码也许不是罪

前言

熟悉我的人都知道,我对代码是有追求的。

正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。

我早期认为:复制代码就是菜。

后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

正文

我总结了一下,主要有以下原因:
1.深刻地理解了源码与测试用例之间的关系。只相信测试用例,而不是相信源码。如果你只改源码而不是补充相应的测试代码,那所谓的有追求是盲目、片面的。

2.对具体的编码实现已经缺乏兴致了。尤其是有AI的情况下,真的是有手就会,何必要我来写呢?我负责设计,再进行 Code Review 不是更好吗?


3.要考虑时间成本。如果别人已经做了一大半,我何必重头开始?

4.提升对质量的认知。聚集于产出质量,而不仅仅是代码质量,那么你就会发现,要做的事还挺多,再考虑时间成本,需要做一个权衡取舍。

上次编辑于:
贡献者: levy
+ diff --git a/daily/dont-try-to-argue-with-a-sb.html b/daily/dont-try-to-argue-with-a-sb.html index 3ac4f5e9..b2ce9ef4 100644 --- a/daily/dont-try-to-argue-with-a-sb.html +++ b/daily/dont-try-to-argue-with-a-sb.html @@ -5,7 +5,7 @@ - 不要与傻逼进行争吵 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

不要与傻逼进行争吵

DailyEmotionWorking ExperienceVideo

不要与傻逼进行争吵

这是个职场吐槽帖,也是个自我反思、情绪管理帖。

我在沟通上,有以下可以改正的点:
0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你

  1. 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你
  2. 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通
  3. 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答
  4. 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!

事情是这样的。

我跟素未谋面的同事在工作上要有一些配合,主要是我提供技术方案、文档给他。

对方工作能力不足就算了,时间管理也不行。8.3号一整天没来找我,8.4号白天也没来找我,快下班了,才打电话跟我说要资料。

这是第一层不满:快下班了,没什么耐心了,我不想别人打扰我周五下班。

电话中,他的语气带着责备,明显是甩锅的意味。找人办事,要别人配合还用这种语气,这是第二层不满。我开始怒他,我说,“兄弟,你冷静下。”

接着,他又跟我扯淡,我一看时间,六点钟了,是时候下班。我就说,“有什么问题发钉钉,我周一给你解决。” 他急了,他说,“你不能下班,你不能走,你要陪我!”
tmd,自己延误工期,还要我陪你加班,哪个这个道理?这是第三层不满,我有点破防了。我开始说一些不太理智、不太专业的话。

我说,“凭什么要我陪你加班?V我50,我就陪你”。他说,“你在搞笑吧,不如让领导V你50?” 
他接着说,“这个东西很急,客户今天晚上就要!” 我说,“那是你的事,你的客户,又不是我的客户。”

这两句话一出,味道就有点不对了。本来全然是他不占理的,现在他抓住的我语言上漏洞,开始要拉我下水了。

于是马上拉群、打字,向领导打小报告,一幅小学生向老师告状的模样。

这情商,这行为,我们还能好好配合?我们接下来净在群里互怒,要么是甩锅,要么是暗示对方能力不行,没有一个人在解决问题。

最后,我回去以后,还是带着不满加了班。不但自己加,还搞得我的 leader 也跟着加,让 leader 对那个人也无语,心情不好了。

这一点,我有点自责,因为我连累了我的 leader。而这也是我为什么反思并把这件事情写出来的原因,自己不爽只是自己的事,连累到他人,我就过意不去了。

我不希望这种事情再次发生!我应该能掌握事态,Let me take control of things!

下次再有这种事,一定是先把工作上的问题解决,然后再远离傻逼。不要在傻逼身上浪费时间,多说一句话、甚至只是骂一句都不值得!

想一想,已经几年没跟人因为工作的事情跟人发生明面上的不愉快了。我的情绪管理能力似乎下降了,这也是个契机,让我重新对这方面重视起来。

经验上看,除了开头的句式转换以外,其实有一个心法值得学习。那就是永远不要跟人在明面上闹不愉快

同时,结合“阳奉阴违”的技巧,你不用去做口舌之争,你去做你的事就行了。
比如,我准备下班了,对方说,“你不要下班,你不能走。” 我可以回复,“好的,我不走,我上个厕所。” 然后我就挂掉电话,下班走人,这不是很简单吗?何必反问他“为什么我不能走?”没有意义啊!

总之,明面上不与人闹翻,保持自己情绪的稳定,一方面是职业素养体现,另一方面也是自己涵养的体现。
还有一方面,那就是有利于身心健康——生气是会对身体器官造成伤害的,如高血压、胃疼、肠胃不适!

上次编辑于:
贡献者: levy
- +
跳至主要內容

不要与傻逼进行争吵

DailyEmotionWorking ExperienceVideo

不要与傻逼进行争吵

这是个职场吐槽帖,也是个自我反思、情绪管理帖。

我在沟通上,有以下可以改正的点:
0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你

  1. 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你
  2. 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通
  3. 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答
  4. 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!

事情是这样的。

我跟素未谋面的同事在工作上要有一些配合,主要是我提供技术方案、文档给他。

对方工作能力不足就算了,时间管理也不行。8.3号一整天没来找我,8.4号白天也没来找我,快下班了,才打电话跟我说要资料。

这是第一层不满:快下班了,没什么耐心了,我不想别人打扰我周五下班。

电话中,他的语气带着责备,明显是甩锅的意味。找人办事,要别人配合还用这种语气,这是第二层不满。我开始怒他,我说,“兄弟,你冷静下。”

接着,他又跟我扯淡,我一看时间,六点钟了,是时候下班。我就说,“有什么问题发钉钉,我周一给你解决。” 他急了,他说,“你不能下班,你不能走,你要陪我!”
tmd,自己延误工期,还要我陪你加班,哪个这个道理?这是第三层不满,我有点破防了。我开始说一些不太理智、不太专业的话。

我说,“凭什么要我陪你加班?V我50,我就陪你”。他说,“你在搞笑吧,不如让领导V你50?” 
他接着说,“这个东西很急,客户今天晚上就要!” 我说,“那是你的事,你的客户,又不是我的客户。”

这两句话一出,味道就有点不对了。本来全然是他不占理的,现在他抓住的我语言上漏洞,开始要拉我下水了。

于是马上拉群、打字,向领导打小报告,一幅小学生向老师告状的模样。

这情商,这行为,我们还能好好配合?我们接下来净在群里互怒,要么是甩锅,要么是暗示对方能力不行,没有一个人在解决问题。

最后,我回去以后,还是带着不满加了班。不但自己加,还搞得我的 leader 也跟着加,让 leader 对那个人也无语,心情不好了。

这一点,我有点自责,因为我连累了我的 leader。而这也是我为什么反思并把这件事情写出来的原因,自己不爽只是自己的事,连累到他人,我就过意不去了。

我不希望这种事情再次发生!我应该能掌握事态,Let me take control of things!

下次再有这种事,一定是先把工作上的问题解决,然后再远离傻逼。不要在傻逼身上浪费时间,多说一句话、甚至只是骂一句都不值得!

想一想,已经几年没跟人因为工作的事情跟人发生明面上的不愉快了。我的情绪管理能力似乎下降了,这也是个契机,让我重新对这方面重视起来。

经验上看,除了开头的句式转换以外,其实有一个心法值得学习。那就是永远不要跟人在明面上闹不愉快

同时,结合“阳奉阴违”的技巧,你不用去做口舌之争,你去做你的事就行了。
比如,我准备下班了,对方说,“你不要下班,你不能走。” 我可以回复,“好的,我不走,我上个厕所。” 然后我就挂掉电话,下班走人,这不是很简单吗?何必反问他“为什么我不能走?”没有意义啊!

总之,明面上不与人闹翻,保持自己情绪的稳定,一方面是职业素养体现,另一方面也是自己涵养的体现。
还有一方面,那就是有利于身心健康——生气是会对身体器官造成伤害的,如高血压、胃疼、肠胃不适!

上次编辑于:
贡献者: levy
+ diff --git a/daily/index.html b/daily/index.html index 3c512484..36e9f141 100644 --- a/daily/index.html +++ b/daily/index.html @@ -34,10 +34,10 @@ } - + - - + + diff --git a/daily/iteration-retrospective-of-sanyuan.html b/daily/iteration-retrospective-of-sanyuan.html index 75bfb2cc..80b1756c 100644 --- a/daily/iteration-retrospective-of-sanyuan.html +++ b/daily/iteration-retrospective-of-sanyuan.html @@ -5,7 +5,7 @@ - 迭代复盘之三员管理 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

迭代复盘之三员管理

DailyWorking Experience

迭代复盘之三员管理

前言

本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。

这次迭代因为各种原因,延期了快一个星期(周六还加了班)。

那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。

动手前,至少梳理出接口清单

太轻信旧代码了,抱着代码迁移过来,跑起来就能用的态度,没能及时发现功能遗漏点,到了提测才发现有功能未实现,这是可以避免的。

更优的工作流应该是:

  1. 先进行业务梳理
  2. 整理出接口清单
  3. 再进行代码迁移
  4. 根据接口清单,逐个验证迁移的代码是否符合需求

迁移时,采取结队编程

看到有 500 个文件要迁移,觉得用分治的思路去实现会更高效,但这种思维有一个误区:做得快不等于有效率,因为效率需要的是有用功,而不是无用功。

实践表明,初期对业务的理解、代码的熟悉程度还不够深入,迁移代码遇到分歧点时,个人在独自处理时容易产生错误——而这种错误并不自知,这才是最蛋疼的地方。

所以,我才建议,采取结队编程实践,两个人一起来迁移代码,这样子遇到分歧时可以讨论,而不是贸然行动。表面上看,这样的进程慢了,实际上却能提高对代码、业务的理解,提高了迁移的正确率,真正地提高效率。

迁移后,需要对自己负责的功能设计测试用例

这里的本质,是不能完全依赖别人。虽然测试人员会编写测试用例,也会进行用例评审,但这里会有两个问题:

  1. 前期评审时,自己的心思可能在理解业务、功能设计与改造以及代码迁移,没有多少心思能去 debug 用例的精细程度
  2. 测试也是人,也会有遗漏,并且没有接触源码,有些特殊的、极端的边界场景,自己作为研发人员,更容易发现

可以看成,我们自己设计的用例是对测试人员给出的测试用例的补充。

当然,由于时间的关系,做完善的测试一般都不太可能了。
对于 legacy code,想补充 service 层的单元测试成本是很高的。写 controller 层的测试,可能也没有太多时间。
但至少应该用 postman 进行调试,或者在用户界面上点几下,不能完全等着前端或测试人员反馈,认为没有反馈就是没有 bug。

上次编辑于:
贡献者: levy
- +
跳至主要內容

迭代复盘之三员管理

DailyWorking Experience

迭代复盘之三员管理

前言

本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。

这次迭代因为各种原因,延期了快一个星期(周六还加了班)。

那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。

动手前,至少梳理出接口清单

太轻信旧代码了,抱着代码迁移过来,跑起来就能用的态度,没能及时发现功能遗漏点,到了提测才发现有功能未实现,这是可以避免的。

更优的工作流应该是:

  1. 先进行业务梳理
  2. 整理出接口清单
  3. 再进行代码迁移
  4. 根据接口清单,逐个验证迁移的代码是否符合需求

迁移时,采取结队编程

看到有 500 个文件要迁移,觉得用分治的思路去实现会更高效,但这种思维有一个误区:做得快不等于有效率,因为效率需要的是有用功,而不是无用功。

实践表明,初期对业务的理解、代码的熟悉程度还不够深入,迁移代码遇到分歧点时,个人在独自处理时容易产生错误——而这种错误并不自知,这才是最蛋疼的地方。

所以,我才建议,采取结队编程实践,两个人一起来迁移代码,这样子遇到分歧时可以讨论,而不是贸然行动。表面上看,这样的进程慢了,实际上却能提高对代码、业务的理解,提高了迁移的正确率,真正地提高效率。

迁移后,需要对自己负责的功能设计测试用例

这里的本质,是不能完全依赖别人。虽然测试人员会编写测试用例,也会进行用例评审,但这里会有两个问题:

  1. 前期评审时,自己的心思可能在理解业务、功能设计与改造以及代码迁移,没有多少心思能去 debug 用例的精细程度
  2. 测试也是人,也会有遗漏,并且没有接触源码,有些特殊的、极端的边界场景,自己作为研发人员,更容易发现

可以看成,我们自己设计的用例是对测试人员给出的测试用例的补充。

当然,由于时间的关系,做完善的测试一般都不太可能了。
对于 legacy code,想补充 service 层的单元测试成本是很高的。写 controller 层的测试,可能也没有太多时间。
但至少应该用 postman 进行调试,或者在用户界面上点几下,不能完全等着前端或测试人员反馈,认为没有反馈就是没有 bug。

上次编辑于:
贡献者: levy
+ diff --git a/daily/leverage-ai-to-boost-coding-productivity.html b/daily/leverage-ai-to-boost-coding-productivity.html index 266afae6..c6daf2b1 100644 --- a/daily/leverage-ai-to-boost-coding-productivity.html +++ b/daily/leverage-ai-to-boost-coding-productivity.html @@ -5,7 +5,7 @@ - 都什么年代了,还在用传统方式写代码? | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

都什么年代了,还在用传统方式写代码?

AIDaily

都什么年代了,还在用传统方式写代码?

前言

还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

本文将分享 AI 时代的编程新实践,教你如何从一个 "Ctrl + C"、 "Ctrl + V" 工程师,变成一个 "Tab + Enter" 工程师🤣。

开发流程

软件的一般研发流程为:

  1. 需求分析
  2. 程序设计
  3. 代码编写
  4. 软件测试
  5. 部署上线

我们在这里主要关心步骤2~4,因为与 AI 结合得比较紧密。虽然需求分析也可以借助 AI,但不是本文的重点,故不做讨论。

程序设计

经过需求分析、逻辑梳理后,在编写实际代码前,需要进行程序设计。

此环节的产物是设计文档,是什么类型的设计文档不重要,重要的是伪代码的输出。

虽然《Code Complete》早就推荐过伪代码的实践,但对此人们容易有一个误区:认为写伪代码花的时间,已经够把实际代码写好了。但 AIGC 时代,此问题可以轻松破解:AI 写代码的速度肯定比人快,因此,只要能找到方法能让 AI 生成符合需求的代码,就值得花时间去研究。而伪代码,就是让 AI 快速生成符合期望的实际代码的最好方式。

为什么这么说呢?因为想要让 AIGC 符合期望,恰当的 Prompt 必不可少。但如何写好这个 Prompt,需要提供多少上下文,才能让 AI 更好地理解我们的意图,这是需要技巧、需要调试的。而经过精心设计的伪代码,本身已经提供了足够的上下文,且意图足够聚焦,减轻了对 Prompt 的要求,又提高了 AIGC 的成功率。

我们来看一下伪代码示例:

plainText = JSON.stringify(data)
+    
跳至主要內容

都什么年代了,还在用传统方式写代码?

AIDaily

都什么年代了,还在用传统方式写代码?

前言

还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

本文将分享 AI 时代的编程新实践,教你如何从一个 "Ctrl + C"、 "Ctrl + V" 工程师,变成一个 "Tab + Enter" 工程师🤣。

开发流程

软件的一般研发流程为:

  1. 需求分析
  2. 程序设计
  3. 代码编写
  4. 软件测试
  5. 部署上线

我们在这里主要关心步骤2~4,因为与 AI 结合得比较紧密。虽然需求分析也可以借助 AI,但不是本文的重点,故不做讨论。

程序设计

经过需求分析、逻辑梳理后,在编写实际代码前,需要进行程序设计。

此环节的产物是设计文档,是什么类型的设计文档不重要,重要的是伪代码的输出。

虽然《Code Complete》早就推荐过伪代码的实践,但对此人们容易有一个误区:认为写伪代码花的时间,已经够把实际代码写好了。但 AIGC 时代,此问题可以轻松破解:AI 写代码的速度肯定比人快,因此,只要能找到方法能让 AI 生成符合需求的代码,就值得花时间去研究。而伪代码,就是让 AI 快速生成符合期望的实际代码的最好方式。

为什么这么说呢?因为想要让 AIGC 符合期望,恰当的 Prompt 必不可少。但如何写好这个 Prompt,需要提供多少上下文,才能让 AI 更好地理解我们的意图,这是需要技巧、需要调试的。而经过精心设计的伪代码,本身已经提供了足够的上下文,且意图足够聚焦,减轻了对 Prompt 的要求,又提高了 AIGC 的成功率。

我们来看一下伪代码示例:

plainText = JSON.stringify(data)
 digest = hash(plainText) // 防篡改
 secret = Symmetric.getKey()  
 cipherText = encryptText(data, secret) // 防内容泄密
@@ -155,6 +155,6 @@
     }
 }
 

把上述代码 copy 下来,放到工程中,根据需要改造即可。

这里特别要说下,强烈推荐使用原版 AI,而不是寻找平替,同样的 prompt,下图是某一中文平替输出的结果:
只生成了函数声明,没有生成函数实现。二者对比,未免相形见绌。

辅助编程工具

改造的过程中,少不了 AI pair programming tools。对此,我推荐使用 Amazon 的 CodeWhisperer,原因很简单,跟 GitHub Copilot 相比,它是免费的😃。

CodeWhisperer 的安装可以看文末的安装教程,我们先来看一下它是怎么辅助我们编码的。

第一种方式是最简单的,那就是什么都不管,等待智能提示即可,就好像 IDEA 原来的提示一样,只不过更智能。

下图示例中,要把原来的中文异常提示,修改成英文,而我只输入了两个字符 IM, 就得到了智能提示,补全了完整的英文字符串!

可以注意到,上图的智能建议一共有 5 条,相应的快捷键为:

  1. 方向键 ->,查看下一条提示
  2. 方向键 <-,查看上一条提示
  3. Tab,采用该提示
  4. Esc,拒绝提示

我们再来看第二种 CodeWhisperer 的使用方式,编写注释,获得编码建议。

最后一种就是编写一个空函数,让 CodeWhisperer 根据函数名去猜测函数的实现,这种情况需要足够的上下文,才能得到令人满意的结果。

软件测试

AI 生成的内容,并不是完全可信任的,因此,单元测试的重要性变得尤为突出。

对上述代码编写测试代码后,实际上并不能一次通过,因为前面 AI 生成的代码参数有误。

此时需要一边执行单测,一边根据结果与 AI 进行交互:

经过修改,最终测试用例通过👏!

总结

本文通过案例,展示了 AI 如何结合软件研发的流程,提升我们的编程效率的。

其中,个人认为最重要的是编写伪代码与进行单元测试。有趣的是,这两样实践在 AIGC 时代之前,就已经被认为是最佳实践。这给我们启示:某些方法论、实践经得起时间的考验,技术更新迭代,它们历久弥新。

对于AI编程的到底应该怎么看,其实 GitHub Copilot 的产品标语已经很好地概括了:Your AI pair programmer。在结队编程里,由两个人一起来进行编程活动。一个是 Navigator,负责指导、审查,另一个 Driver,负责具体的代码编写。AI 的出现,其实是取代了 Driver 的角色,而另一个充当 Navigator 角色的人,仍然是不可或缺的。

最后,AI 是否能进一步渗透我们的工作流,还有待探索。此文作引抛砖引玉之用,期待大家的后续分享。

附:CodeWhisperer 安装

下载 2023 年的 IDEA,打开 Plugins Marketplace,找到 AWS Toolkit

安装完成、重启 IDEA 后,点击左下角,按下图所示操作:

如果第一次使用,就点击 1 处进行注册,如果已经有账号了,就点击 2 处使用自己的账号登录。

注册、登录、授权成功后,出现如图所示页面,即可使用 CodeWhisperer。

上次编辑于:
贡献者: levy
- + diff --git a/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html b/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html index a4a67065..8eddcb28 100644 --- a/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html +++ b/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html @@ -5,7 +5,7 @@ - 微软中国CTO演讲观后感 | levy @@ -34,10 +34,10 @@ } - + - - + + diff --git a/daily/testing-environments-should-be-consistent-with-production-environments.html b/daily/testing-environments-should-be-consistent-with-production-environments.html new file mode 100644 index 00000000..7134b2fc --- /dev/null +++ b/daily/testing-environments-should-be-consistent-with-production-environments.html @@ -0,0 +1,44 @@ + + + + + + + + 生产教训:测试环境要与生产环境一致 | levy + + + + + + +
跳至主要內容

生产教训:测试环境要与生产环境一致

Daily

生产教训:测试环境要与生产环境一致

事件还原

业务流程:

  1. app-a 上传文件
  2. app-b 下载文件后使用文件

其他信息:

  1. 开发、测试环境使用 MinIO
  2. 生产环境使用 Amazon S3

问题:

  1. app-a 上传文件成功
  2. app-b 使用文件报错

逐步分析定位问题:

  1. app-a 与 app-b 配置是否一致?——确认都是使用 S3
  2. S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题
  3. app-a 是否真的上传成功?——确认文件已在 S3
  4. app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。

所以问题就在于,为什么 app-b 的代码会判断文件不存在?

  • 是传过去的路径参数不对?
  • 还是 app-b 的逻辑有问题?

经过确认,传参是正确的,那么只有一个推论: app-b 判断文件是否存在的逻辑有问题。

具体是哪里有问题呢?

原来文件下载前,有一个判断目录是否存在的逻辑,其实现是判断 S3 的对象是否存在,示例代码如下:

return s3Client.doesObjectExist("/path/dir")
+

则该代码永远为 false。

如何修复呢?改为调用 listObjects就可以了。如下图所示(左边是修改前,右边是修改后):
image.png

分析

很难想像,为什么在涉及对象存储的代码中,会有判断目录是否存在的逻辑。

好在我是个善于为别人找理由的人。我观察了下代码库,发现接口与类结构如下:
应该是在写 HDFSStorage 的时候,用到了判断目录是否存在的逻辑,从而把方法提升到了接口定义 IStorage 中。

如果定义 IStorage 与实现 S3Storage 的代码的人并不相同(我相信很可能是这样),那么我更倾向于认为责任在定义 IStorage 的人身上,因为TA并没有合理地设计接口,导致后来者被迫实现没有意义的接口,从而出错。

结论

为什么开发、测试环境没问题?或者说,为什么 MinIO 没这个问题?那是因为实现 MinIOStorage 的人,没有踩这个坑。

所以,本此事件给我来的经验教训是什么呢?既不是 S3 如何判断目录是否存在,也不是接口定义的重要性。而是:要让测试环境与生产环境尽可能保持一致,提前暴露问题。而不是测试环境是这样的配置,生产环境又是那样的配置——这就会加大生产环境出问题的可能性!

上次编辑于:
贡献者: levy
+ + + diff --git a/daily/things-I-have-to-vent-about-vue.html b/daily/things-I-have-to-vent-about-vue.html index 8c8f6601..a1d5c6fc 100644 --- a/daily/things-I-have-to-vent-about-vue.html +++ b/daily/things-I-have-to-vent-about-vue.html @@ -5,7 +5,7 @@ - 对Vue不得不吐槽的事 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

对Vue不得不吐槽的事

FrontendDailyVideo

对Vue不得不吐槽的事

为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!

总结一下,我对Vue生态不满的地方在于:

  1. Vue总是破坏性升级,新技术完全不管旧用户的体验
  2. 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性
上次编辑于:
贡献者: levy
- +
跳至主要內容

对Vue不得不吐槽的事

FrontendDailyVideo

对Vue不得不吐槽的事

为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!

总结一下,我对Vue生态不满的地方在于:

  1. Vue总是破坏性升级,新技术完全不管旧用户的体验
  2. 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性
上次编辑于:
贡献者: levy
+ diff --git a/daily/use-claude2-instead-of-chatgpt.html b/daily/use-claude2-instead-of-chatgpt.html index ffb04b8b..22a6d47a 100644 --- a/daily/use-claude2-instead-of-chatgpt.html +++ b/daily/use-claude2-instead-of-chatgpt.html @@ -5,7 +5,7 @@ - 再见ChatGPT,我选择Claude2! | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

再见ChatGPT,我选择Claude2!

VideoAITool

再见ChatGPT,我选择Claude2!

大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。

首先要评测的当然是ChatGPT了,因为最早用的就是它。

现在来看呢,没有当初那么惊艳了,因为已经有很多的平替了,甚至是超替了。当然现在我还会用它的唯一原因呢,就是这个Voice Control插件。

它不但可以让我进行语音输入,还可以让ChatGPT变成语音输出,那这一点很方便我进行英语口语练习。

当然要说口语练习的话,可以用手机App Call Annie。

不过呢,因为它要求iOS 16,我不想系统升级,所以没有用。另外呢,想练习的话,其实还是要去不断的对自己的词语、句子去做一个调整、优化的,那这一点我还是认为ChatGPT带有文字的输出会更适合我,方便我做笔记。

ChatGPT 注册还是有门槛的。因为大家知道虽然它可以免费使用,但实际上要注册成功的话,要么是买一个账号,要么是买短信。实际上还是付费了。当初的话我是花了16块钱买了个短信,然后注册账号的,现在有很多其他的免费替代方案,所以现在是不推荐注册 ChatGPT 的,毕竟白嫖更香嘛。

那下一个评测对象是 New Bing。New Bing 刚出来时很惊艳,差点成为大家的AI女友。后来微软做了很多调整,现在已经被束缚住了手脚。

当然,从用户界面上来说 New Bing 是有着最佳交互体验的。现在可以上传图片,支持在线链接,也支持粘贴上传,体验感极佳。

然后呢,对于New Bing呢,每回答一个问题,后面会有三个提示的快捷问题选项,可以让我们更方便地去问相关的问题,这也是一个很好的体验。

但实际效果来看,对话有长度限制,当然这是小问题,30次回答已经不少了。

但最蛋疼的是它的自我审察,面对敏感问题,它会避而不答,甚至终止对话。



那这样的话就体验很差了,因此呢,我的评价是绣花枕头,中看不中用。

要想使用 New Bing呢,之前是要去加入 wait list,等待微软的审批。现在据说是放开了,应该是可以更容易注册了。但是考虑到前面描述的不尽如人意的体验,我是认为没有必要去注册了,因为还有更好的。

那么接下来我们要评测的就是ChatGPT的劲敌,原Open AI成员打造的Claude。

Claude 2 有着超长的token,100K。


并且与其他AI相比呢,它有一个特点是比较的健谈。也就是说回答了你一个问题后,他会说,In more detail,接着用更丰富的语言去回答一遍,这其实是一个很好的特性。

那么有没有一款软件,它拥有上述AI的所有的优点呢?有啊,那就是poe.comopen in new window。对于还没有使用AI或者还在使用国产AI的同学呢,我非常推荐使用它,用谷歌账号即可注册使用。

这里面有各种的AI任你挑选。同时呢,他也集成了 New Bing 的快捷回复的选项。所以说,我认为当前最佳的组合就是poe.comopen in new window + claude2,这将是一个体验极佳的使用方案。

最后,来看看面对恶意提问,Claude2是如何回答的:

上次编辑于:
贡献者: levy
- +
跳至主要內容

再见ChatGPT,我选择Claude2!

VideoAITool

再见ChatGPT,我选择Claude2!

大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。

首先要评测的当然是ChatGPT了,因为最早用的就是它。

现在来看呢,没有当初那么惊艳了,因为已经有很多的平替了,甚至是超替了。当然现在我还会用它的唯一原因呢,就是这个Voice Control插件。

它不但可以让我进行语音输入,还可以让ChatGPT变成语音输出,那这一点很方便我进行英语口语练习。

当然要说口语练习的话,可以用手机App Call Annie。

不过呢,因为它要求iOS 16,我不想系统升级,所以没有用。另外呢,想练习的话,其实还是要去不断的对自己的词语、句子去做一个调整、优化的,那这一点我还是认为ChatGPT带有文字的输出会更适合我,方便我做笔记。

ChatGPT 注册还是有门槛的。因为大家知道虽然它可以免费使用,但实际上要注册成功的话,要么是买一个账号,要么是买短信。实际上还是付费了。当初的话我是花了16块钱买了个短信,然后注册账号的,现在有很多其他的免费替代方案,所以现在是不推荐注册 ChatGPT 的,毕竟白嫖更香嘛。

那下一个评测对象是 New Bing。New Bing 刚出来时很惊艳,差点成为大家的AI女友。后来微软做了很多调整,现在已经被束缚住了手脚。

当然,从用户界面上来说 New Bing 是有着最佳交互体验的。现在可以上传图片,支持在线链接,也支持粘贴上传,体验感极佳。

然后呢,对于New Bing呢,每回答一个问题,后面会有三个提示的快捷问题选项,可以让我们更方便地去问相关的问题,这也是一个很好的体验。

但实际效果来看,对话有长度限制,当然这是小问题,30次回答已经不少了。

但最蛋疼的是它的自我审察,面对敏感问题,它会避而不答,甚至终止对话。



那这样的话就体验很差了,因此呢,我的评价是绣花枕头,中看不中用。

要想使用 New Bing呢,之前是要去加入 wait list,等待微软的审批。现在据说是放开了,应该是可以更容易注册了。但是考虑到前面描述的不尽如人意的体验,我是认为没有必要去注册了,因为还有更好的。

那么接下来我们要评测的就是ChatGPT的劲敌,原Open AI成员打造的Claude。

Claude 2 有着超长的token,100K。


并且与其他AI相比呢,它有一个特点是比较的健谈。也就是说回答了你一个问题后,他会说,In more detail,接着用更丰富的语言去回答一遍,这其实是一个很好的特性。

那么有没有一款软件,它拥有上述AI的所有的优点呢?有啊,那就是poe.comopen in new window。对于还没有使用AI或者还在使用国产AI的同学呢,我非常推荐使用它,用谷歌账号即可注册使用。

这里面有各种的AI任你挑选。同时呢,他也集成了 New Bing 的快捷回复的选项。所以说,我认为当前最佳的组合就是poe.comopen in new window + claude2,这将是一个体验极佳的使用方案。

最后,来看看面对恶意提问,Claude2是如何回答的:

上次编辑于:
贡献者: levy
+ diff --git a/daily/vim-creator-pass-away.html b/daily/vim-creator-pass-away.html index 260cfa2c..cde4eea8 100644 --- a/daily/vim-creator-pass-away.html +++ b/daily/vim-creator-pass-away.html @@ -5,7 +5,7 @@ - Vim 作者离世 | levy @@ -34,10 +34,10 @@ } - + - - + + diff --git a/daily/what-is-the-difference-between-sh-and-bash.html b/daily/what-is-the-difference-between-sh-and-bash.html index fdb206e7..7667fd48 100644 --- a/daily/what-is-the-difference-between-sh-and-bash.html +++ b/daily/what-is-the-difference-between-sh-and-bash.html @@ -5,7 +5,7 @@ - sh与bash的区别 | levy @@ -34,10 +34,10 @@ } - + - - + + diff --git a/daily/you-dont-need-to-add-tenant_id-to-every-table.html b/daily/you-dont-need-to-add-tenant_id-to-every-table.html index a5676e6e..388a7660 100644 --- a/daily/you-dont-need-to-add-tenant_id-to-every-table.html +++ b/daily/you-dont-need-to-add-tenant_id-to-every-table.html @@ -5,7 +5,7 @@ - 技术点评:别每张表都加tenant_id | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

技术点评:别每张表都加tenant_id

DesignDaily

技术点评:别每张表都加tenant_id

前言

系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。

背景

在维护旧系统时,踩了各种坑,终于忍不住在群里吐槽了下,于是有以下对话。



既然群友有疑问,我索性就整理了一下,把前因后果清楚。

正文

1.首先,租户数据隔离级别应该如何,没有唯一标准,评价只有是否合适。因此,逻辑隔离、物理隔离,都不是吐槽点。

2.原来的设计是,采取逻辑隔离方案,具体做法是,给每一张表都加上了tenant_id字段。问题就在于,有必要每一张表加吗?系统的权限设计是:先有租户,再有应用,用户、角色、资源、权限设置等内容都挂在应用下,所以,应用下的内容,关联 app_id 就行了,根本不需要tenant_id。

3.冗余多一个字段,会出现什么问题呢?先不提查询性能、存储空间等细节,就说很实际的场景:
3.1 每张表都加 tenant_id,几乎每条 sql 都要加 where tenant_id = ? ,那程序员会怎么做?首先想到的是用框架自动注入 sql
3.2 在后台管理端,有一个超级管理员,能够查询出所有租户的内容,也就是说,此时查询不能带 where tenant_id = ?,也即不能让框架注入 sql

要同时兼容上述逻辑,程序员又会怎么做呢?于是就引入了万恶的全局变量,类似于 injectSql = true,就添加 tenant_id 作为过滤条件。但默认是不是要 injectSql = true 呢?每个项目代码又不一样,你不运行,你都不知道。

更恶心的是,需求变化后,是否需要带上 tenant_id 的逻辑与原来不一致时,你得在某行代码执行前,手动设置 inejctSql 的值,在该行代码之后,再手动复原——因为如果不复原,作为全局变量,会影响到后面的代码!

md,这时候后你才会知道,还不如老老实实地设置 tenant_id,显示地设置,好过这种隐蔽的依赖。

4.但这还不是最难搞的。因为上述的是代码问题,真正难搞的是数据问题。考虑一种场景:超级管理员在后台管理某租户的应用,手动为租户添加数据,请问,新增的数据 tenant_id 的值是什么,某租户的 id,还是超级管理员的id?按逻辑来说,应该是某租户的 tenant_id。但问题在于,由于理解不同,或由于疏忽让框架自动注入了 tenant_id,导致上述场景,有些数据的 tenant_id 是超级管理员的id。而又因为超级管理员进行查询时,是不带 tenant_id 作为过滤条件的,因此即使 tenant_id 的值设置错误,依然在界面上能显示,使得这个问题一直存在着,旧数据一直被保留并使用。

5.现在,有需求要导出某个租户下的数据,结果发现 tenant_id 乱七八糟,你难不难受?

6.那么,梳理完逻辑链条,我认为,虽然某些程序员的在实现上犯了低级错误,但不是主要原因,罪魁祸首应该是设计上的懒惰。设计精细一点,明确好 tenant_id 到哪张表为止,也就没有后面的 sql 注入、数据错误那么多事了。所以,我才说,给租户隔离等于给每张表加 tenant_id 的设计很傻逼!

上次编辑于:
贡献者: levy
- +
跳至主要內容

技术点评:别每张表都加tenant_id

DesignDaily

技术点评:别每张表都加tenant_id

前言

系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。

背景

在维护旧系统时,踩了各种坑,终于忍不住在群里吐槽了下,于是有以下对话。



既然群友有疑问,我索性就整理了一下,把前因后果清楚。

正文

1.首先,租户数据隔离级别应该如何,没有唯一标准,评价只有是否合适。因此,逻辑隔离、物理隔离,都不是吐槽点。

2.原来的设计是,采取逻辑隔离方案,具体做法是,给每一张表都加上了tenant_id字段。问题就在于,有必要每一张表加吗?系统的权限设计是:先有租户,再有应用,用户、角色、资源、权限设置等内容都挂在应用下,所以,应用下的内容,关联 app_id 就行了,根本不需要tenant_id。

3.冗余多一个字段,会出现什么问题呢?先不提查询性能、存储空间等细节,就说很实际的场景:
3.1 每张表都加 tenant_id,几乎每条 sql 都要加 where tenant_id = ? ,那程序员会怎么做?首先想到的是用框架自动注入 sql
3.2 在后台管理端,有一个超级管理员,能够查询出所有租户的内容,也就是说,此时查询不能带 where tenant_id = ?,也即不能让框架注入 sql

要同时兼容上述逻辑,程序员又会怎么做呢?于是就引入了万恶的全局变量,类似于 injectSql = true,就添加 tenant_id 作为过滤条件。但默认是不是要 injectSql = true 呢?每个项目代码又不一样,你不运行,你都不知道。

更恶心的是,需求变化后,是否需要带上 tenant_id 的逻辑与原来不一致时,你得在某行代码执行前,手动设置 inejctSql 的值,在该行代码之后,再手动复原——因为如果不复原,作为全局变量,会影响到后面的代码!

md,这时候后你才会知道,还不如老老实实地设置 tenant_id,显示地设置,好过这种隐蔽的依赖。

4.但这还不是最难搞的。因为上述的是代码问题,真正难搞的是数据问题。考虑一种场景:超级管理员在后台管理某租户的应用,手动为租户添加数据,请问,新增的数据 tenant_id 的值是什么,某租户的 id,还是超级管理员的id?按逻辑来说,应该是某租户的 tenant_id。但问题在于,由于理解不同,或由于疏忽让框架自动注入了 tenant_id,导致上述场景,有些数据的 tenant_id 是超级管理员的id。而又因为超级管理员进行查询时,是不带 tenant_id 作为过滤条件的,因此即使 tenant_id 的值设置错误,依然在界面上能显示,使得这个问题一直存在着,旧数据一直被保留并使用。

5.现在,有需求要导出某个租户下的数据,结果发现 tenant_id 乱七八糟,你难不难受?

6.那么,梳理完逻辑链条,我认为,虽然某些程序员的在实现上犯了低级错误,但不是主要原因,罪魁祸首应该是设计上的懒惰。设计精细一点,明确好 tenant_id 到哪张表为止,也就没有后面的 sql 注入、数据错误那么多事了。所以,我才说,给租户隔离等于给每张表加 tenant_id 的设计很傻逼!

上次编辑于:
贡献者: levy
+ diff --git a/english/contemporary-college-english-1.html b/english/contemporary-college-english-1.html index 99fa6418..2f4fbbc9 100644 --- a/english/contemporary-college-english-1.html +++ b/english/contemporary-college-english-1.html @@ -5,7 +5,7 @@ - 现代大学英语精读(第2版)第一册 | levy @@ -34,10 +34,10 @@ } - +
跳至主要內容

现代大学英语精读(第2版)第一册

English

现代大学英语精读(第2版)第一册

介绍

全书链接:https://www.ximalaya.com/album/43891910open in new window

虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。

虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。

值得一读的文章

本册里面大部分是记叙文,阅读趣味性比较强。

欧亨利不愧是大师,第一册里收录了两篇他的小说。

另外,戏剧 The Monsters Are Due in Maple Street 也值得一读

总结一下,本册难度较低,就是来建立信心的。如果想只阅读精华,那只看我挑选出的三篇文章就够了。适应了全英文阅读后,就可以开始看第二册的内容了。

上次编辑于:
贡献者: levy
- + diff --git a/english/contemporary-college-english-2.html b/english/contemporary-college-english-2.html index 1f6b99d3..49d6c0a0 100644 --- a/english/contemporary-college-english-2.html +++ b/english/contemporary-college-english-2.html @@ -5,7 +5,7 @@ - 现代大学英语精读(第2版)第二册 | levy @@ -34,10 +34,10 @@ } - +
跳至主要內容

现代大学英语精读(第2版)第二册

English

现代大学英语精读(第2版)第二册

介绍

全书链接:https://www.ximalaya.com/album/44290107open in new window

本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。

值得一读的文章

say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂

原文:https://www.missmccalister.com/uploads/3/0/9/3/30937509/lesson-7a-say-yes-by-tobias-wolff.pdfopen in new window

The Dog of Pompeii 讲述了一个盲人男孩与一只小狗的故事,令人泪目。结尾比较含蓄,思考片刻明白后,就破防了~

原文:https://www.acaedu.net/cms/lib3/TX01001550/Centricity/Domain/562/Week%206%20-%20The%20Dog%20of%20Pompeii.pdfopen in new window

button button 人心拷问:如果按下按钮,你能得到一笔钱,但世上会有一人因此而死去,你会按下按钮吗?

原文:https://christian_fuller.myteachersite.org/teacher/files/documents/button%20button.pdfopen in new window

A Doctor's Dilemma 医生的困境

the-oyster-and-the-pearl

解析:https://schoolworkhelper.net/william-saroyans-the-oyster-and-the-pearl-summary-analysis/open in new window

文册收录了两篇奥巴马的演讲,还是挺有意思的,能打动人心的部分当然有,同时也可以看出一些政治家演讲的特色:

  1. 赞美对方。糖衣炮弹过去,对方就伸手不打笑脸人
  2. 用好词,美化自己,把功劳往自己身上揽
  3. 强调双方关系: 我们是很友好的哦、我们是合作伙伴哦
  4. 回顾历史,展望未来:我们已经走了很长的路,做出了很多的改变,取得了很大的进步,为了下一代、为了未来,我们要加油鸭!

相关链接:

另外,我摘录了一些觉得不错的句子:

  • When dealing with people, let us remember we are not dealing with creatures of logic.
  • We are dealing with creatures of emotion, creatures bristling with prejudices, and motivated by pride and vanity.
  • Any fool can criticize, condemn and complain—and most fools do. But it takes character and self-control to be
    understanding and forgiving.
上次编辑于:
贡献者: levy
- + diff --git a/english/contemporary-college-english-3.html b/english/contemporary-college-english-3.html index 9ac7b8cd..9a09d85d 100644 --- a/english/contemporary-college-english-3.html +++ b/english/contemporary-college-english-3.html @@ -5,7 +5,7 @@ - 现代大学英语精读(第2版)第三册 | levy @@ -34,10 +34,10 @@ } - +
跳至主要內容

现代大学英语精读(第2版)第三册

English

现代大学英语精读(第2版)第三册

介绍

全书链接:https://www.ximalaya.com/album/44439108open in new window

阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。

这种转变,说明了两点:

  1. 我开始应用我在中文阅读中培养出来的阅读喜爱,去评判英文文章了
  2. 我开始有意识地运用阅读技巧,恰当地泛读,不再是拿到一篇文章就逐字逐句地读

值得一读的文章

A-DILL-PICKLE 讲述的是曾经的恋人偶遇,欧亨利式结尾:

原文:https://www.katherinemansfieldsociety.org/archive/www.katherinemansfieldsociety.org/assets/KM-Stories/A-DILL-PICKLE1917.pdfopen in new window

The Invisible Japanese Gentlemen

原文:https://www.ff.umb.sk/app/cmsFile.php?disposition=a&ID=4292open in new window

My Grandmother, the Bag Lady 被这篇文章打动,泪目了

The End of the Civil War 本册收录了挺多美国南北战争相关的内容,这是其中角度新颖的一篇

Why Historians Disagree 科普了一下历史学是如何看待历史,有助于我们更理性地学习历史

the-most-dangerous-game 可以说是三册中最精彩的一篇小说之一,不容错过

原文+分析(左边是内容,右边是分析):https://www.litcharts.com/lit/the-most-dangerous-game/summary-and-analysisopen in new window

The Bench 用一个黑人小哥故意坐在标明只能由欧洲人坐的板凳上的故事,以小见大,描绘了 Civil War 之后美国种族歧视现状

Twelve Angry Men 十二怒汉,是本册的精彩内容之一。激发了对法治话题的兴趣,看得我直接睡不着,接着去B站看电影版。

电影:https://www.bilibili.com/bangumi/play/ep332629?theme=movieopen in new window

上次编辑于:
贡献者: levy
- + diff --git a/english/contemporary-college-english-4.html b/english/contemporary-college-english-4.html index af7a42b2..8a838fe0 100644 --- a/english/contemporary-college-english-4.html +++ b/english/contemporary-college-english-4.html @@ -5,7 +5,7 @@ - 现代大学英语精读(第2版)第四册 | levy @@ -34,10 +34,10 @@ } - +
跳至主要內容

现代大学英语精读(第2版)第四册

English

现代大学英语精读(第2版)第四册

介绍

全书链接:https://www.ximalaya.com/album/44641280open in new window

本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。

值得一读的文章

Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从

Economic Growth Is a Path to Perdition, Not Prosperity 提醒我们更加理性地看待“增长”

The Damned Human Race 其实我觉得本文并非“值得一读”,但还列举出来原因有二:一是毕竟是马克·吐温之作,不可怠慢;二是我认为其论点新颖,不愧为讽刺大师,但论据越到后面越无说服力,可列为“反面教材”

A String of Beads 挺有趣的文章,揭示了人们普遍的一种心理:事情的真相相比,人们更在意的是故事的戏剧性,或是否符合自己心中的设定

The World House 马丁·路德·金 的文章

Soldier's Heart 也叫PTSD,由战后士兵自述

Secrets 这是一篇可以让人由好奇变为感动的文章。我原以为讲的是主人公的 “aunt” 与其旧情人的故事,直到最后,“forgive”一词的出现,令我恍然大悟,瞬间泪目。为避免剧透,我不细说了,强烈推荐。

The Rivals
两个男的在言语上较劲,只觉得好笑,可看到后面,我心情如此图1657000860176_9DFDD7F8-C809-4aa4-A996-05F4C984C76A.png
强烈推荐,你一定也会“surprised”!

Cord:一对母女的故事

论文分析。有一定难度,因为它是综合了同一个作者的三篇文章进行分析,需要挑选着读:https://www.yuque.com/office/yuque/0/2022/pdf/160590/1657283857640-11aa59bb-edc1-475f-a97f-37124a27afd9.pdfopen in new window

The Never-Ending Fight “机器人学之父”阿西莫夫的反迷信文章

Man of the Moment 又到了令人享受的戏剧欣赏时间

Is Everybody Happy 讨论了幸福的定义

上次编辑于:
贡献者: levy
- + diff --git a/english/contemporary-college-english-5.html b/english/contemporary-college-english-5.html index d56a0f37..76db5f92 100644 --- a/english/contemporary-college-english-5.html +++ b/english/contemporary-college-english-5.html @@ -5,7 +5,7 @@ - 现代大学英语精读(第2版)第五册 | levy @@ -34,10 +34,10 @@ } - +
跳至主要內容

现代大学英语精读(第2版)第五册

English

现代大学英语精读(第2版)第五册

介绍

全书链接:https://www.ximalaya.com/album/49466046open in new window

本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。

Who Are You and What Are You Doing Here

原文链接:https://m.kekenet.com/daxue/201909/593904.shtmlopen in new window

本文分享了作者教育观,并阐述了上大学的意义,否定了上学是为了就业的观点。

摘录如下: Education has one salient enemy in present-day America, and that enemy is education——university education in
particular. To almost every university education is a means to an end. For students, that end is a good job.

The best reason to read great writers is to see if they may know you better than you know yourself. You may find your
own suppressed and rejected thoughts flowing back to you with an "alienated majesty." Reading the great writers, you may
have the experience that Longinus associated with the sublime: You feel that you have actually created the text
yourself. For somehow your predecessors are more yourself than you are.

Trying to figure out whether the stuff you're reading is true or false and being open to having your life changed is a
fraught, controversial activity. Doing so requires energy from the professors. This kind of perspective-altering
teaching and learning can cause the things which administrators fear above all else: trouble, arguments, bad press, etc.

Two Kinds

原文链接:https://m.kekenet.com/daxue/201909/595380.shtmlopen in new window

本文以女儿的视角讲述了母亲望女成凤而女儿叛逆的故事,文中母亲代表的中国式家庭教育观,我想不少人会感同身受。用现在的眼光看来,这只不过是一段充满了失败的沟通、无法双赢的家庭教育经历,借助非暴力沟通或双赢思维是能够避免的。

摘录如下: I didn't budge. And then I decided. I didn't have to do what my mother said anymore. I wasn't her slave. This
wasn't China. I had listened to her before and look what happened. She was the stupid one.

You want me to be someone that I'm not!" I sobbed. "I will never be the kind of daughter you want me to be!"

"Only two kinds of daughters," she shouted in Chinese. "Those who are obedient and those who follow their own mind! Only
one kind of daughter can live in this house. Obedient daughter!"

"Then I wish I wasn't your daughter, I wish you weren't my mother," I shouted.

Love is a Fallacy

原文链接:https://www.ximalaya.com/sound/414753193open in new window

这多篇看下来,这篇是目前最有意思、最有趣、最令人忍俊不禁的。为避免剧透,我就少说一点。本文首尾响应,完美闭环,看完后我直呼我直呼,哈哈哈哈。就说到这了,总之墙裂推荐。

Rewriting American History

原文链接:https://www.ximalaya.com/sound/414754775open in new window

本文讲述的是美国历史教材的一些变化及背后的思考。我认可其中的一个做法:不是对历史给一个所谓正统的结论,而是充分准备材料,从不同的角度陈述观点,引导人思考。

Nobel Peace Price About Global Warming

原文链接:https://www.ximalaya.com/sound/414756339open in new window

The Bluest Eyes

原文链接:https://www.ximalaya.com/sound/414757228open in new window

这是个记叙文,然而,我认为确是这么多篇文章中,最难懂的。

How News Becomes Options and Opinions Off-Limits

原文链接:https://www.ximalaya.com/sound/414759448open in new window

本文说明了当代一种社会现代:人们在看新闻,不是在看“事实”,而是在看“观点”,作者认为,现在的新闻跟小说并非没有相似之处了。然而,作者对这种现象并非完全持否定态度,他认为这是言论自由的一种方式。最后,作者还表达了在自由社会对“尊重”一词的看法。

摘录如下: In any version of a free society, the value of free speech must rank the highest, for that is the freedom without
which all other freedoms would fall.

In free societies, you must have the free play of ideas. There must be arguments, and it must be impassioned and
untrammeled.

A free society is not a calm and eventless place——that is the kind of static, dead society dictators try to create. Free
societies are dynamic, noisy, turbulent and full of radical disagreements.

The Indispensable Opposition

原文链接:https://m.kekenet.com/daxue/202001/603745.shtmlopen in new window

本文所表达的观点,属于民主与自由思想的经典内容:不可缺少的反对派(或忠诚的反对派)。

摘录如下: I wholly disapprove of what you say, but will defend to the death your right to say it.

If we truly wish to understand why freedom is necessary in a civilized society, we must begin by realizing that, because
freedom of discussion improves our own opinions, the liberties of other men are our own vital necessity.

We must insist that free oratory is only the beginning of free speech; it is not the end, but a means to an end. The end
is to find the truth.

The only reason for dwelling on all this is that if we are to preserve democracy we must understand its principles. And
the principle which distinguishes it from all other forms of government is that in a democracy the opposition not only
is tolerated as constitutional but must be maintained because it is in fact indispensable.

The democratic system cannot be operated without effective opposition. For, in making the great experiment of governing
people by consent rather than by coercion, it is not sufficient that the party in power should have a majority. It is
just as necessary that the party in power should never outrage the minority. That means that it must listen to the
minority and be moved by the criticisms of the minority. That means that its measures must take account of the
minority's objections, and that in administering measures it must remember that the minority may become the majority.

The Danger of a Single Story

原文链接:https://www.ximalaya.com/sound/414765357open in new window

认识到硬币有正反面,并坚持凡事至少从两个角度去看待与分析,那么本篇文章就没白读。

摘录如下: It is impossible to talk about the single story without talking about power. Power is the ability not just to tell
the story of another person, but to make it the definitive story of that person.

All of these stories make me who l am. But to insist on only these negative stories is to flatten my experience and to
overlook the many other stories that formed me.

The single story creates stereotypes,and the problem with stereotypes is not that theyare untrue, but that they are
incomplete. They make one story become the only story.

The consequence of the single story is this: It robs people of dignity. It makes our recognition of our equal humanity
difficult. It emphasizes how we are different rather than how we are similar.

Come Rain or Come Shine

原文链接:https://www.ximalaya.com/sound/414767340open in new window

看得不太懂,但故事倒挺有意思的。

解析:https://medium.com/the-afterglow/come-rain-or-come-shine-by-kazuo-ishiguro-finding-the-moment-b09e418652dbopen in new window

Invisible Man

原文链接:https://www.ximalaya.com/sound/414776047open in new window

节选自同名小说(有改编),telling a story about a black teenager present his speech before white people.

You've Got to Find What You Love

原文链接:https://www.ximalaya.com/sound/414769322open in new window

Steve Jobs 在 Standford 的演讲,没想到 Jobs 声音这么年轻!演讲分享三个故事,表达主旨“follow your heart”。

精彩摘录如下: You can't connect the dots looking forward: You can only connect them looking backwards. So you have to trust
that the dots will somehow connect in your future. You have to trust in something-your destiny,life,whatever. Because
believing that the dots will connect down the road will give you the confidence to follow your heart, even when it leads
you off the well-worn path. And that would make all the difference.

Sometimes life is gonna hit you in the head with a brick. Don't lose faith. I'm convinced that the only thing that kept
me going was that I loved what I did. You've got to find what you love. And that is as true for your work as it is for
your lovers. Your work is going to fill a large part of your life, and the only way to be truly satisfied is to do what
you believe is great work. And the only way to do great work is to love what you do. If you haven't found it yet, keep
looking, and don't settle. As with all matters of the heart, you'll know when you find it. And like any great
relationship, it just gets better and better as the years roll on. So keep looking. Don't settle.

I have looked in the mirror every morning and asked myself: "If today were the last day of my life,would I want to do
what l am about to do today?" And whenever the answer has been "No" for too many days in a row, l know I need to change
something. Remembering that I'll be dead soon is the most important tool I've ever encountered to help me make the big
choices in life. Because almost everything-all external expectations, all pride, all fear of embarrassment or
failure-these things just fall away in the face of death,leaving only what is truly important. Remembering that you are
going to die is the best way I know to avoid the trap of thinking you have something to lose. You are already naked.
There is no reason not to follow your heart.

Where Do We Go from Here

原文链接:https://www.ximalaya.com/sound/414771197open in new window

本文是马丁·路德·金的另一篇呼吁黑人平等的演讲,从中可以看到与《I Have a Dream》的影子。

摘录如下: As long as the mind is enslaved, the body can never be free. Psychological freedom,a firm sense of self-esteem, is
the most powerful weapon against the long night of physical slavery. No Lincolnian emancipation proclamation or
Johnsonian civil rights bill can totally bring this kind of freedom. The Negro will only be free when he reaches down to
the inner depths of his own being and signs with the pen and ink of assertive manhood his own emancipation proclamation.

And so I say to you today that I still stand by nonviolence. For through violence you may murder a murderer but you
can't murder murder. Through violence you may murder a liar but you can't establish truth. Through violence you may
murder a hater,but you can't murder hate. Darkness cannot put out darkness. Only light can do that.

上次编辑于:
贡献者: levy
- + diff --git a/english/contemporary-college-english-6.html b/english/contemporary-college-english-6.html index 3fb07e41..97598074 100644 --- a/english/contemporary-college-english-6.html +++ b/english/contemporary-college-english-6.html @@ -5,7 +5,7 @@ - 现代大学英语精读(第2版)第六册 | levy @@ -34,10 +34,10 @@ } - +
跳至主要內容

现代大学英语精读(第2版)第六册

English

现代大学英语精读(第2版)第六册

前言

全书链接:https://www.ximalaya.com/album/49468954open in new window

本册是整个系列的最后一册了,完结撒花🎉

Paper Tigers

原文链接:https://www.ximalaya.com/sound/414998175open in new window

探讨了 Asian American 的教育经历与社会成就不符的现象。

摘录如下: Let me summarize my feelings toward Asian values: Damn filial piety. Damn grade grubbing. Damn Ivy League mania.
Damn deference to authority. Damn humility and hard work. Damn harmonious relations. Damn sacrificing for the future.
Damn earnest, striving middle-class servility.

Maybe a traditionally Asian upbringing is the problem. In order to be a leader, you must have followers. Associates are
initially judged on how well they do the work they are assigned. But being a leader requires different skill sets. “The
traits that got you to where you are won't necessarily take you to the next level," says the diversity consultant Jane
Hyun, who wrote a book called Breaking the Bamboo Ceiling.

At Yale, Chua made the connection between her upbringing and her adult dissatisfaction. “My parents didn't sit around
talking about politics and philosophy at the dinner table," she told the students. Even after she had escaped from
corporate law and made it onto a law faculty,“I was kind of lost. I just didn't feel the passion."

Chua's Chinese education had gotten her through an elite schooling, but it left her unprepared for the real world.

注:Chua
也就是《虎妈战歌》的作者:https://baike.baidu.com/item/%E8%94%A1%E7%BE%8E%E5%84%BF/3424632#7open in new window

What Is News

原文链接:https://www.ximalaya.com/sound/414999846open in new window

提示我们如何看待新闻,与前面的如何看待历史可谓是姐妹篇。

摘录如下: The news is made rather than gather.

We see what we expect to see. We focus on what we are paid to see. And those who pay us to see usually expect us to
accept their notions.

“What is news?" News,we might say, may be history in its first and best form, or the stuff of literature, or a record of
the condition of a society, or the expression of things, but in its worst form it can also be mainly a “filler,” a
“come-on" to keep the viewer's attention until the commercials come.

All of which leads us to reiterate, first, that there are no simple answers to the question “What is news?"
And, second, that it is not our purpose to tell you what you ought to believe about the question. The purpose of this
chapter is to arouse your interest in thinking about the question. Your answers are to be found by knowing what you feel
is significant and how your sense of the significant conforms with or departs from that of others. Answers are to be
found in your ideas about the purpose of public communication, and in your judgment of the kind of society you live in
and wish to live in. We cannot provide answers to these questions. But you also need to know something about the
problems, limitations, traditions, motivations, and, yes, even the delusions of the television news industry.

At War with the Planet

原文链接:https://www.ximalaya.com/sound/415001340open in new window

本文为了我们科普了人类社会与自然环境的特征,提供了一个新颖而深刻的视角来看待人与自然的关系,启发我们在采取人与自然和谐相处的措施时,不要走极端。

How to Get the Poor off Our Conscience

原文链接:https://www.ximalaya.com/sound/415004615open in new window

本文讨论了面对一直存在的贫富差距现象,社会思想、政府措施在历史上经历了怎样的变化。

Housewifely Arts

原文链接:https://www.ximalaya.com/sound/415006607open in new window

本文主要表达的是对母亲的回忆,其实属于可看可不看类型,之所以还放上来,原因有二:

  1. 冷幽默,毕竟有几处地方让我笑了
  2. 引以为戒,千万别学女主

The One Against The Many

原文链接:https://www.ximalaya.com/sound/415007539open in new window

本文主要是讲美国文明的,没有一个地方出现China,但几乎处处都能看到China的影子。绝对不容错过的好文,可以说是反洗脑的基础。

精彩摘录: It is important here to insist on the distinction between ideals and ideology. Ideals refer to the long-run goals
of a nation and the spirit in which these goals are pursued. Ideology is something different, more systematic, more
detailed, more comprehensive, more dogmatic.

An ideology, in other words, is an abstraction from reality. There is nothing wrong with abstractions or models. In
fact, we could not conduct discourse without them. The ideological fallacy is to forget that ideology is an abstraction
from reality and to regard it as reality itself. The besetting sin of the ideologist, in short, is to confuse his own
tidy models with the vast, turbulent, unpredictable, and untidy reality which is the stuff of human experience.

Consider for a moment the ideologist's view of history. The ideologist contends that the mysteries of history can be
understood in terms of a clear-cut, absolute, social creed which explains the past and forecasts the future. Ideology
thus presupposes a closed universe whose history is determined, whose principles are fixed, whose values and objectives
are deducible from a central body of social dogma and often whose central dogma is confided to the custody of an
infallible priesthood.

The American tradition has found this view of human history repugnant and false, against the belief in the
all-encompassing power of a single explanation, against the commitment to the absolutism of ideology, against the notion
that all answers to political and social problems can be found in the back of some sacred book, against the
deterministic interpretation of history.

Ideologists are afraid of the free flow of ideas, even of deviant ideas within their own ideology. They are convinced
they have a monopoly on the Truth. Therefore they always feel that they are only saving the world when they slaughter
the heretics. Their objective remains that of making the world over in the image of their dogmatic ideology. The goal is
a monolithic world, organized on the principle of infallibility-but the only certainty in an absolute system is the
certainty of absolute abuse.

The goal of free men is quite different. Free men know many truths, but they doubt whether any mortal man knows the
Truth. Their religious and their intellectual heritage join in leading them to suspect fellow men who lay claim to
infallibility. They believe that there is no greater delusion than for man to mistake himself for God. They accept the
limitations of the human intellect and the infirmity of the human spirit.

Notes on the English Character

原文链接:https://www.ximalaya.com/sound/415017440open in new window

本文对英格兰人的性格特征进行了简单的探讨。

The Death of a Pig

原文链接:https://www.ximalaya.com/sound/415019607open in new window

本文主人公本计划养一只猪来吃,却养死了。为何把这篇文章收录进来呢,可能是因为里面细节写得好吧,把主人公的病急乱投医的场景写得较为生动。

原文链接:https://www.ximalaya.com/sound/415020743open in new window

Princeton学长回校演讲,核心观点是:你以为你的成功全靠实力,然而其中隐含着运气。

The Accidental Universe

原文链接:https://www.ximalaya.com/sound/415022329open in new window

本文是物理相关的说明文,讲了多重宇宙假设,提出一种观点:我们所在的宇宙是出于偶然。

Rowling's Speech at Harvard

原文链接:https://www.ximalaya.com/sound/415018537open in new window

罗琳的英音很好听,而且本次演讲也很幽默,建议边看边听。

上次编辑于:
贡献者: levy
- + diff --git a/english/everyone-can-learn-english-1-overview.html b/english/everyone-can-learn-english-1-overview.html index 79f419ce..ea74ac2c 100644 --- a/english/everyone-can-learn-english-1-overview.html +++ b/english/everyone-can-learn-english-1-overview.html @@ -5,7 +5,7 @@ - 人人都能学会的英语1:开篇 | levy @@ -34,10 +34,10 @@ } - +
跳至主要內容

人人都能学会的英语1:开篇

English

人人都能学会的英语1:开篇

为什么学

不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。

也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语:

  • 出国
  • 去外企
  • 有国际化需求
  • 学习专业领域的前沿知识

我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。

此时也许有人会担心,自己已错过了最佳的学习时机,现在太迟了吧?非也。种一棵树最好的时机是10年前,其次是现在。如果把人生当成马拉松,那么现在开始,一定来得及,绝不会晚。记住,Better late than never!

怎么学

关于怎么学的问题,我认为需要内外兼修。

建设心态

在意识到、并承认掌握英语的必要性后,我们需要真正地相信:现在行动并不晚。

如果这点不能相信,那说什么都没用,因为人会找各种借口让自己放弃,如“现在没时间,还有更重要的事”、“性价比太低了”、“以后再说吧”、“用中文也行”。如果是这样,那事情注定是要失败的。

其次,要学会耐心。人们常常高估一个月所能做的事,却低估一年所能做的事。不要浮躁,学会接受每天进步一点点:

有了耐心,才能在整个过程中,抵制住营销号“神奇方法”的诱惑。记住,耐心是美德,这在别的领域也适用。

与此同时,在学习过程中,注意建立自己的信心。从怀疑自己是否能,到相信自己能,最后内化成不再需要强调这件事。

提升认知

一定要搞明白:英语是技能,不只是知识。技能意味着,不仅要输入,还要输出,且需要反复地练习。

所以,在学习过程中,一定要让输入与输出闭环:如果想提高听力,就要辅以口语练习;如果想提高阅读能力,就要做相应的写作练习。

整个过程中,强调的是实践,是行动力,而不是记忆力。不要妄想把大量的知识装入脑子里——那是没用的,因为它不属于你。只有经过练习、能说出来或写出来的东西,才是你的,才会内化成自己的能力。

以上认知很重要,如果不能达成共识,那后面的方法对你是无效的。

明确意义

需要问一下自己,英语对自己而言到底是什么?

是考试、考证上分的手段,还是升职加薪的工具,抑或是丰富自己、了解另一种文化、开阔视野的方式?

上面只是举例,并且例子之间并不冲突,是可以兼容的。

总之,对这个问题的回答,反映了个人动机,对行动力有很大的影响。

制定计划

整个学习过程是超过一个月的,对于这种时长的事件,是需要制定计划的。

说直白了,就是要拆分目标,评估工作量,然后按计划执行,并能在实践中结合自身情况动态调整。这种能力,不仅是学英语,学任何东西都是适用的。

培养习惯

注意培养自己的习惯,利用惯性帮助自己坚持,减少半途而废的可能。

建议固定时间、固定地点来学习,可以结合自己的日常作息规律,养成一个新的习惯。如每天早上起来,或晚上洗完澡后,固定抽时间来学习。

注重方法

前面说了那么多,都是内功,放在前面,是因为我认为心态建设、认知及习惯比方法更重要。

至于学习方法,因学习内容而异。按照知识体系划可大致分为:

  • 基础知识:
    • 音标
    • 单词
    • 语法
  • 综合能力
    • 听说
    • 读写

基础知识是一定要掌握的,而对于综合能力,基于实用主义,不同目的可有不同的侧重。简单来说就是:

  • 如果需要利用英文资料学习、工作,或想阅读英文原著,或只需与人进行文字交流,应侧重读写
  • 如果需要与人进行即时言语沟通,或想看“生肉”,或想听英文有声读物,应侧重听说

今天主要是作为开篇,做个介绍,具体的方法,会在后续的内容中逐步展开。

上次编辑于:
贡献者: levy
- + diff --git a/english/everyone-can-learn-english-2-pronunciation.html b/english/everyone-can-learn-english-2-pronunciation.html index dd5ebacb..b7c952fe 100644 --- a/english/everyone-can-learn-english-2-pronunciation.html +++ b/english/everyone-can-learn-english-2-pronunciation.html @@ -5,7 +5,7 @@ - 人人都能学会的英语2:音标 | levy @@ -34,10 +34,10 @@ } - +
跳至主要內容

人人都能学会的英语2:音标

English

人人都能学会的英语2:音标

方法

音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。

我推荐根据赖世雄的《美语音标》进行学习:

  • 在微信公众号 常春藤英语集团 买相关书籍
  • 在 喜马拉雅 或 B站 听相关音频,进行跟读练习
  • 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格

有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。

我给出了我之前的学习进度记录,仅供参考
image.png

学完之后,如果有兴趣,可以了解下进阶内容。American pronunciation workshop:https://www.bilibili.com/video/BV1qE411v7oEopen in new window
(我特意找了没有字幕的视频,建议就这样看。如果是侧重读写的同学,直接跳过就好)

再给出关于音标的一些资料:

讨论

这里我补充几点,虽然不是核心,却与主题息息相关:

  1. 是否要买实体书?
  2. 为什么是美语,而不是“英语”
  3. 关于口音(accent)

建议买实体书

现在网络这么发达,信息随处可得,所以很可能你不愿意买实体书。

这样也行,但我仍然建议买书,反正我买了全套,因为我认为从某种意义上讲,实体书有助于提升行动力。还记得前面我们讲到过,整个学习过程强调的是实践,行动力或执行力是很重要的。而购买的动作,本身就是执行力的一种表现。

而相对的,白嫖的行为背后,隐含着不愿意付出的意味,这种思维本身是不可取的。当然,这也许是我过度解读,你可以继续白嫖。

不想买实体书的另一个原因是,书多了不方便,比如搬家很麻烦。对这个问题,我是这样处理的,仅供参考:

  • 捐书
  • 送人

美语是主流

也许有人认为英音更好听,某些时候,我也赞同这个观点。但我们还是实用主义,现在西方世界谁说话最大声,大家都清楚。毕竟“日不落”已然衰落,现在还是美刀更好使(还在说刀的事,手动滑稽)!所以,美语才是主流。

一定要学英音的,建议通关后重修发音,或现在就另找门派学习。

不要纠结口音

在国内的网络平台上,很常见的场景是一群人对某个中国人的口音评头论足,说什么“这个不正宗、不地道啊”,“你这个也不像美音呀”、”工地散装英语哈哈哈“。

我都不知道这些人在纠结什么。谁说话没口音呢?你作为中国人,能跟新闻联播主持人一样说普通话吗?你不也带点地方特色?就算是美国人,不同的地区,也有不同的口音,你说谁才是正宗的呢?

语言最重要的功能是沟通,能让别人听懂是首要需求——如果你的口音并不影响别人听懂并理解你,那就不用纠结它。这也是前面为什么我建议下载翻译软件的原因,让机器来判断你发音是否正确,免去担心自己口音的困扰。

总结一下,今天分享了音标学习的方法与经验,后面将分享单词的学习。

上次编辑于:
贡献者: levy
- + diff --git a/english/everyone-can-learn-english-3-words.html b/english/everyone-can-learn-english-3-words.html index 46131ccf..256adae4 100644 --- a/english/everyone-can-learn-english-3-words.html +++ b/english/everyone-can-learn-english-3-words.html @@ -5,7 +5,7 @@ - 人人都能学会的英语3:单词 | levy @@ -34,10 +34,10 @@ } - +
跳至主要內容

人人都能学会的英语3:单词

English

人人都能学会的英语3:单词

方法

单词是听说读写的基础,是有必要花时间去学习的。

然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议:

  1. 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握
  2. 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。
  3. 只推荐利用词根记忆(当然并非百分百有效)
  4. 学习软件:欧路词典——免费,全平台通用

以手机欧路词典为例,打开后,点击“学习”,选择适合自己的单词本(或先做测试让软件来推荐),进行学习即可。
image.png
注意结合个人情况,进行学习设置,例如学习新词数量及、复习数量。
image.png
就我个人而言,我的做法是把全部单词刷一遍,过滤掉我完全掌握的,然后再进行新词的复习。

提醒

必须要给大家一个提醒,避免进入单词学习的陷阱:那就是忍受不了单词学习进度缓慢、记了又忘的情况,于是追求“超级记忆术”,把单词一股脑装在脑子里。

回顾前面所说,学习英语强调的是实践,而不是记忆。所以,不论记忆方法有多高明,它只能解决输入,不能解决输出的问题。因此,我们的重点应该放在输入与输出闭环上,而不是追求单方面“多快好省”。

网络平台上很多鼓吹类似于“一天背六千个单词”的营销内容,就是抓住了人们没有正确理解的情况下贪多求快的心理,一定要警惕。

再者,单词与阅读理解,并非简单的线性对应关系,即不是单词量学得越多,阅读理解越深。另一方面来说,也不是只有单词量“爆表”,才能进行全英文阅读。

以我自身为例,我可以阅读原著,但我的词汇量不过四千左右。

我之前在某个词汇量评测网站做过评估:http://testyourvocab.com/result?user=13229458open in new window
image.png

4360 是网站为我估算的单词数量。

为求精确,我还找了一本考研(包含了高考、四级、六级、考研词汇)的单词本,把上面的所有单词刷了一遍,结果如下:
image.png
上图的含义为:

  • 0 个未学习,意思是单词本的所有单词我都遇到了一遍了,并且我都进行了掌握程度判断
  • 已掌握 3844,意是这些单词我一看就能知道是什么意思,并且能做翻译
  • 学习中 4122,具体又可细分为:
    • 认识:一词多义,仅知道部分的中文含义,当其在另一个句子中体现另一种意思时,自己不知道
    • 模糊:原先不知道中文含义,但根据例句能隐约推断出其含义
    • 不认识:给了例句也看不懂

从上面我对学习中的单词的分类就可以看出,单词这件事,不是非黑即白的。而把单词掌握数量直接与英语能力挂钩,也是不可取的。

我在学生时代,其实没有买过一本单词本,也没有刻意地去背过单词(例如每天固定背50个),所以上述词汇量,还是挺客观的。就以词汇量而言,可能很多人都比我强——既然单词对我来说不是困扰,那对大家来说,应该也不是。我想这点信心,大家是可以有的。

总之,我想说的是,单词重要,却也不用刻意强调“背”这件事。把单词与英语能力直接挂钩,追求神奇的背单词的方法,这是很多人学英语的误区。而在这种误区下,就催生了速成的方法论,迎合了浮躁社会下急功近利的心态。但其实,强调实践,讲究输入输出闭环,才是更符合客观规律的学习方式。

记住:重要的不是你记住了多少单词,而是你能运用多少单词来造句。

延伸阅读:刷7000个单词的经验总结

上次编辑于:
贡献者: levy
- + diff --git a/english/everyone-can-learn-english-4-listening-and-speaking.html b/english/everyone-can-learn-english-4-listening-and-speaking.html index c26eb35e..a7d6ff61 100644 --- a/english/everyone-can-learn-english-4-listening-and-speaking.html +++ b/english/everyone-can-learn-english-4-listening-and-speaking.html @@ -5,7 +5,7 @@ - 人人都能学会的英语4:听说 | levy @@ -34,10 +34,10 @@ } - +
跳至主要內容

人人都能学会的英语4:听说

English

人人都能学会的英语4:听说

前言

听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。

方法

听说属于综合能力,建议遵循以下学习步骤:

  1. 激发兴趣,建立信心
  2. 明确方向
  3. 脚踏实地,坚持输入并输出

1.建立信心

首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料:

前者是西游记英文动画版,后者是BBC的职场英语动画片,任选其一即可(如果是实用主义,当然选第二个啦)。
两个材料都是只有英文字幕的,要逐渐习惯这种模式。这就是为什么说学习时要注重心理建设,因为需要走出舒适区。

记住,上面给的材料,主要是帮助自己建立自信的, 学到什么不重要,重要的是你觉得自己行了,可以进行下一步了。

2.明确方向

在进行深入学习前,我们需要明确自己的学习方向,究竟是 Daily English 还是 Business English?

当然可以全部都学,但那是结果,在过程中总要分个前后。对这个问题的回答,取决于读者自己。

Daily English 我推荐:https://www.youtube.com/c/EnglishSpeakingCourses/search?query=conversationopen in new window
这个视频如果看不了,可以看我写的教程:https://www.yuque.com/levy/blog/how-to-surfopen in new window

Business English,我推荐 eslpod 的教材:

  1. Using English at Workopen in new window
  2. English for Business Meetingsopen in new window
  3. Interview Questions Answeredopen in new window

这里特别说下,eslpod 的创始人是英语方面的语言学及教育学的博士,可以说没有人比他更懂英语教学(手动滑稽)!跟着他学,我成功地喜欢上了美语,从此以后听加州口音会觉得最舒服。

以上三本教材我自己买了,有需要的可以私信我发你。

另外,赖世雄的《高级美语》也极力推荐,它是属于综合性的教材,内容方面起到开阔视野的作用。而我推荐它的原因是,本书北美外教的发音尤为悦耳,朗读课文时简直“说得比唱得还好听”,练习听说的同学一定不要错过。

3.坚持输入并输出

有了材料还远远不够,只是在听、光在那里看,结果还是哑巴英语。因此,对于无论什么材料,我建议这样使用:

  1. 不看课本(字幕),全部语音听完
  2. 看课本(字幕),做笔记,学习单词与短语
  3. 慢速跟读
    1. 看课本(字幕),播一句,暂停,跟读一句
    2. 不看课本(字幕),播一句,暂停,跟读一句
    3. 看课本(字幕),不暂停,课程跟读
  4. 独自念出来,并使用手机软件(如微信)录音,听一听自己说的效果,并进行语音翻译,以检测自己的输出正确率
  5. 脱稿表达(此步骤可选)

在这里分享一个我遇到的“困境”。在进行不看课本的慢速跟读的练习时,播一句跟读一句的形式,让我很容易犯困。但我没有因此就放弃,也没想办法强行提神,而是转而把它当作催眠工具——困了就睡觉。这样过了两周,有一天晚上,我没有犯困,做完了训练。而那一天也是我走出“困境”的日子,后面的日子里,再也没犯困过!

其实我不止有一次类似的“犯困”经历,都是与学习新东西有关的。我想,这可能是大脑在生理上对学习的抗拒。对于新领域的知识或不同寻常的学习方式,是需要走出舒适区的,而这种“犯困”可能是大脑潜意识在拒绝走出舒适区。对此,应对方法很简单,那就是坚持下去,每天固定时间执行相应的学习动作,直到大脑潜意识“屈服”,转而适应新的东西。

以上就是今天要分享的关于听说练习的内容,下一期将分享英语读写的学习经验。

上次编辑于:
贡献者: levy
- + diff --git a/english/everyone-can-learn-english-5-reading-and-writing.html b/english/everyone-can-learn-english-5-reading-and-writing.html index c87b7d0e..189df66b 100644 --- a/english/everyone-can-learn-english-5-reading-and-writing.html +++ b/english/everyone-can-learn-english-5-reading-and-writing.html @@ -5,7 +5,7 @@ - 人人都能学会的英语5:读写 | levy @@ -34,10 +34,10 @@ } - +
跳至主要內容

人人都能学会的英语5:读写

English

人人都能学会的英语5:读写

前言

读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。

阅读能力升级之旅

我先给出自己经历过的全英阅读能力的变化过程,仅供参考:

  1. 看到英文网站,第一反应是点击切换中文版
  2. 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读
  3. 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域)
  4. 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读
  5. 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读
  6. 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了

这是怎么做到的呢?我主要采用了以下方法。

阅读方法

建立信心

如果之前读者并没有全英文的阅读体验,建议先根据兴趣选一本书(或阅读材料)后,在两周内读完它——无论用什么手段,一定要在该时间段内完成。此举似小实大,有两点内涵:

  • 时长再拉长,你很可能读了后面忘前面;时间越长,越拖延,则两相结合,很可能最终放弃了阅读
  • 在较短的时间内读完,能快速建立信心,为后续阅读打下基础

第一本书或阅读材料很关键,感兴趣是最重要的,当然也可以结合后面提到的蓝思值进行选材。

我当初使用的是 轻听英语 app,在上面看完了英文文字加语音版的死亡笔记。看完后感受如下:

  • 一开始的阅读速度比音频的播放速度还要慢,如果没有兴趣,很可能就没耐心了
  • 因为是动漫的文字版,除了画外音(内心的独白),几乎全是人物的对话,这些内容是很容易理解的

之后我信心大涨,用一周时间,把魔兽世界官方小说《Arthas: Rise of the Lich King》原版看完了。
image.png

根据蓝思值选书

有了信心之后,我们就可以科学地、有节奏地培养自己的英文阅读能力了。

核心思路是评估自己的阅读蓝思值open in new window,再找到适合自己的阅读材料。什么是蓝思值呢?
image.png
假设你的阅读水平是 600L,那么蓝思值在 550L~700L 的阅读材料比较适合你。再低就太简单,达不到提升的效果;再高就超出阅读能力太多,很可能读不下去(因为我们在讲全英文阅读,所以我就不翻译了)。
image.png

综上所述,推荐按以下步骤提升阅读能力:

  1. 评估自己的阅读能力 https://readtheory.org/open in new window image.png
  2. 找到自己感兴趣的读物,通过网站判定书籍的蓝思值是否合适 https://hub.lexile.com/find-a-book/book-resultsopen in new window

相关的 app 我推荐: 轻听英语,里面的书都是免费的。当然大家也可以推荐别的 app,直接有标明蓝思值的就最好不过了。
![lADPJw1WS0BSWVnNBQDNBMw_1228_1280.jpg](https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427501518.pngopen in new window](https://cdn.nlark.com/yuque/0/2022/jpeg/160590/1656413042641-47c45b34-b4d2-4197-9af8-536ea2e8b4be.jpeg#averageHue=%23f0edeb&clientId=u576bfa2c-89a1-4&from=paste&height=471&id=u89b97437&name=lADPJv8gUK04WVbNBQDNAz4_830_1280.jpg&originHeight=1280&originWidth=830&originalType=binary&ratio=1&rotation=0&showTitle=false&size=204047&status=done&style=none&taskId=udfd982fe-6ea1-4219-89f5-d3a68b12c42&title=&width=305.3333740234375open in new window)

巧查生词

阅读的一个拦路虎是生词。大家的第一反应是认为生词太多,看不懂,很容易放弃。

首先,我推荐欧路词典。如果是在电脑上看 pdf,鼠标选中区域就能查词或翻译,非常方便。

再者,表达一个观点:有些单词不认识,甚至有些句子无法理解,并不影响对情节的核心内容的理解。事实上,除去欧亨利式结尾的短篇小说,书的内容越多,其核心内容越不容易受生词影响。因为主题是贯穿全文的,前面因为生词没看懂,后文换了个说法,你就能看得懂了!

因此,对生词,我建议:一页只查一个生词。它的意义是:

  1. 你不会因为频繁查生词而影响阅读体验。事实上,试图把每一个生词都查一遍,是阅读不下去的主要原因!
  2. 因为查词机会的“稀缺性”,你把机会留给你认为最有价值的生词
  3. 强迫你的大脑思考,如何在带着许多“迷雾”的情况下,去理解句子、段落,抓住故事的核心情节,理解书籍的中心思想

在阅读《Arthas: Rise of the Lich King》的时候,我的体验就是:

  • 有些形容词或副词不认识,并不影响理解句子的大意
  • 阅读几章后,为加快阅读速度,只出现一次的生词,我不再查字典——我只查出现过至少两次的单词
  • 再看到后面,有些单词不认识,我都懒得查了,反正不影响我对整体剧情的理解

阅读材料推荐

如果实在找不到全英阅读材料,我推荐看《现代大学英语精读(第2版)》, 我已经挑选了值得一读的文章,英语高考有120分或过了四级的人,应该可以直接点击查看

写作

阅读是输入,写作是输出,二者是相辅相成的。 对于写的能力,推荐从读书笔记开始做练习 (就像看完中文书那样做笔记),也可以找机会与人发邮件。

读书笔记示例:

indispensable-opposition.png
indispensable-opposition.png

这是我购买 eslpod.comopen in new window 教材时发的邮件:
image.png

这是我申请 JetBrains 正版授权时发的邮件:
image.png

还有一种方法, 那就是利用软件的翻译功能,把自己写的中文笔记,翻译成英文,再去校对、修改。很多文本类 Web 应用都有此功能,可以利用起来。
这种方法本质就是,自己一开始不知道英文文章写什么,那就从校对、修改开始做起;自己不能写完整的英文文章,那就从摘取段落开始做起。适应这个过程后,写作的障碍就减少了。

总结一下,英文读写的提升需要踏出舒适区,要耐着性子,不要怕慢。坚持去做,量变终会有质变!

上次编辑于:
贡献者: levy
- + diff --git a/english/how-to-self-evaluate-english-level.html b/english/how-to-self-evaluate-english-level.html index 6404ed84..70d1e7bf 100644 --- a/english/how-to-self-evaluate-english-level.html +++ b/english/how-to-self-evaluate-english-level.html @@ -5,7 +5,7 @@ - 英文能力评测手把手教学 | levy @@ -34,10 +34,10 @@ } - +
跳至主要內容

英文能力评测手把手教学

English

英文能力评测手把手教学

前言

之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。

单词量

进入网站:https://preply.com/en/learn/english/test-your-vocabopen in new window

看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。
image.png
在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。
image.png
对于该结果,网站有以下值得注意的解释:

  1. 结果偏差在正负10%左右:

Your vocabulary count has a margin of error of approximately ±10%. We also round results above 10,000 to the nearest 100, and results above 300 to the nearest 10.

  1. 英语非母语者,用单词量作为能力标准通常划分如下:
image.png
image.png
  1. 结果一年测一次最有效
image.png
image.png

阅读能力

评估阅读能力也就是评估蓝思值。进入网站:https://readtheory.org/open in new window

右上角点击注册:
image.png

选择作为学生:
image.png

可以直接使用谷歌账号注册:
image.png
如果不能谷歌,可以看我写的谷歌教程:https://www.yuque.com/levy/blog/how-to-surfopen in new window
或者自己填表单,进入下一步。

确保自己至少有20分钟的空闲时间,之后点击已经准备好了:
image.png

开始做题,一共8道,看不懂就乱选,后面的题目会根据你的答题情况自动调整:
image.png

做完后,点击确认:
image.png

点击下图红框处:
image.png

往下翻阅,即可看到自己的蓝思值初步评分:
image.png

再给出通用的蓝思值能力对照表:
image.png
以上就是今天的英文能力评测手把手教学啦,希望对大家有所帮助。

上次编辑于:
贡献者: levy
- + diff --git a/english/index.html b/english/index.html index 1febe37e..3a3e91f8 100644 --- a/english/index.html +++ b/english/index.html @@ -34,10 +34,10 @@ } - + - + diff --git a/english/learning-7000-words-task-completed.html b/english/learning-7000-words-task-completed.html index d4bed338..c848e726 100644 --- a/english/learning-7000-words-task-completed.html +++ b/english/learning-7000-words-task-completed.html @@ -5,7 +5,7 @@ - 完成刷7k单词任务 | levy @@ -34,10 +34,10 @@ } - +
跳至主要內容

完成刷7k单词任务

English

完成刷7k单词任务

image.png
这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。

首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。

再者,说明下为什么要做这件事。主要原因有三:

  1. 主动扩充词汇量,与英文阅读相辅相成。诚然,可以一边阅读一边积累词汇,但这种方式我认为是“被动”的,并且有看我之前英文阅读经验分享的朋友,会知道我阅读时查词的频率很低的,如此增加词汇量的效率太慢。另外,词汇又有高频与低频、重点与非重点之分,而这是单词本的强项,因此利用单词本是接触“精华词汇”的高效方式。
  2. 我想知道自己确切的词汇量。虽然在之前分享的在线工具中,我评测过自己的词汇量(7000),但自己感觉有点“虚”,一方面误差有10%,另一方面我觉得波动太大了(两年前测是4000),不太相信,于是打算用刷单词本的“笨方法”去确认自己的词汇量。
  3. 我想体验一下刷单词本的经历。这点我在之前的文章中也透露过,我在学生时代是没有买过单词本、也没有拿着单词本去背诵过,基本都是靠音标+词根,边学课文时边记忆,讲究的是一个自然,没有刻意。然而,这隐约有种“不踏实”之感,没有经过刻意练习,似乎得来的单词“不会珍惜”,于是便趁这个机会把这个经历补上。

最后,我想强调的是,注意我“刷单词”,而不是“背单词”这种描述上的差别。“刷”重在强调,过一遍,有个印象、能认识;“背”强调的是死记硬背,机械地记忆——虽然我认为练习需要刻意,但却不认为它等同于死记。

很多人在学习单词觉得很痛苦,半途而废,很大的原因是强调背,把内容强塞进脑子里很让人痛苦;而背了又忘,更是让人感到强烈的挫败感。在这里,我想分享的经验是,此时需要进行思维的转换:你允许自己忘记某些单词。可以把新单词想像成陌生人,总有人能与你成为好友,总有人不能给你留下深刻印象,总有人与你相处不愉快——这么多单词,你先认识容易认识的,很难“相处”、容易忘记的单词,后面再说呗,没必要一口气吃成胖子。

为了减少痛苦,我有以下经验,它们是属于启发式的(heuristic),仅供参考。每个人应总结符合自己的经验,其核心思想是分层次、分类,不要一刀切。

对单词的掌握程度区分优先级:

  • 读:阅读时能看懂是什么意思;就算第一眼看不出单词意思,结合上下文能推测出意思才行
  • 听:别人正常语速、慢速说时,自己能听出这是哪个单词
  • 说:我不对此有强制要求,看着音标能发音即可,因为当前我的英语实践场景中,并不侧重说
  • 写:此优先级最低,因为写作不是考试,我完全可以查词典,因此有些词很难记我就索性不记,如官僚主义(bureaucratism)、资产阶级(bourgeois)——我能听、阅读,但放弃掌握写

对词意进行分类掌握:有的词十几个意思,你哪能一下子全记住?

  • 结合相应的例句,一次只记一个意思
  • 常见的词意,优先记住
  • 冷门的词意,可以跳过
  • 另外,有些中文词意解释很牵强,可以找英英释义

对词汇进行分类掌握:识别出单词属于哪个领域的

  • 计算机、法律、经济、日常生活、文学等领域的词汇,我放在较高优先级,尽量去掌握
  • 医学领域、非考试重点词汇,我不太重视,能认识就认识,觉得认识上有困难就跳过

当然,单词不能纯刷,要结合阅读一起来做。我选择的是外国语学院的英语专业教材《现代大学英语精读》,二者结合,相辅相成。

还有就是,最好固定时间段,以便形成习惯,如上班坐地铁,或晚饭后等较规律的时间段。

最后,我想说的是,刷了这本单词本,并不意味着我就 master 7,000 English words。它对我而言,更多的是在英语学习经历上,具有里程碑的意义。回顾一下,从2022年2月份开始到,到现在历经大约7个月,我算是把英语单词这件事的“遗憾”给补上了,此事至此告一段落,我可以开始新的旅程了🎉。

上次编辑于:
贡献者: levy
- + diff --git a/english/let-chatgpt-be-your-foreign-language-teacher.html b/english/let-chatgpt-be-your-foreign-language-teacher.html index 96f7e362..33082839 100644 --- a/english/let-chatgpt-be-your-foreign-language-teacher.html +++ b/english/let-chatgpt-be-your-foreign-language-teacher.html @@ -5,7 +5,7 @@ - 让 ChatGPT 成为你的外语私教 | levy @@ -34,13 +34,13 @@ } - +
跳至主要內容

让 ChatGPT 成为你的外语私教

EnglishAI

让 ChatGPT 成为你的外语私教

前言

有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。

本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。

准备工作

在开始之前,要准备好几样东西:

  1. ChatGPTopen in new window, 如果没有账号或不能上网,请查看上网教程
  2. Chrome 浏览器插件 voice-control-for-chatgptopen in new window
  3. 口语练习题,根据个人需求查找即可,下文将以雅思open in new window为例进行说明

安装好插件后,打开 chatGPT 界面,下方就会出现语音输入按钮。
image.png

常用Prompt

下面总结了常用的 Prompt,可以有根据需要进行使用或调整。

设置角色:

  1. Please act as an English teacher.
  2. Please act as an English-speaking test examiner.
  3. Please act as IELTS speaking test examiner.

进入一问一答模式:

  1. You're supposed to asked me questions and wait for my answer. The next question is: xxx

对回答进行完善:

  1. Please revise my answer
  2. Please modify my answer to make it more fluent

对回答进行评分:

  1. Please rate my answer

进行对话

第一句话,是设置好 AI 的角色,让它扮演口语考官。

可以使用以下 prompt:

act as an English-speaking test examiner
 

image.png
可以看出,语音转文字出现错误,单词 IELTS 始终未能正确识别,但 ChatGPT 却能明白其中的意思。

开启语音插件的意义在于,如果语音识别不了自己说的话,很有可能是自己的发音有问题,起到提醒自己纠正发音的作用。另外,ChatGPT 回复的文字,也会转换成语音输出,顺便练习了听力。

根据练习材料,让 AI 问自己问题。
image.png
记得让 AI 对自己的回答评分,可以使用以下 prompt:

please rate my answer after I answer the question each time
 
image.png
image.png

进行下一个问题:
image.png
上述回答不太好,AI 给出了理由:
image.png

修改后再回答,有所进步
image.png

再问下一个问题:
image.png
这个回答同样不理想,但看了提示也不知道要怎么改:
image.png

此时可以新建一个聊天窗口,让 AI 提供示例回答:
image.png

根据示例答案,结合关键词,重新组织语言,切换回原聊天窗口,再回答一次:
image.png
有所进步!

有时对话长了,AI 会“糊涂”如下所示:
image.png

此时要重新强调它扮演的角色,让其回忆起上下文,可以使用以下 prompt:

focus on the speaking test and assume that you ask me this question
 
image.png
image.png

除此之外,就没啥值得注意的了。重复上述过程,不断练习即可。

记录回答

做完了练习,还要作笔记。但在记录回答之前,还要润色一下,毕竟口语表达的时候,可能会存在语法错误。

进入 https://quillbot.com/open in new window,把答案复制上去,先进行语法检查:
image.png
再进行流畅度润色:
image.png
最后保存到笔记本上即可。

上次编辑于:
贡献者: levy
- + diff --git a/frontend/index.html b/frontend/index.html index 59aabdb8..6873e573 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -34,10 +34,10 @@ } - + - - + + diff --git a/frontend/old-articles.html b/frontend/old-articles.html index ed4b57bb..e488746c 100644 --- a/frontend/old-articles.html +++ b/frontend/old-articles.html @@ -5,7 +5,7 @@ - 旧文章精选 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

旧文章精选

Frontend

上次编辑于:
贡献者: levy
- +
跳至主要內容

旧文章精选

Frontend

上次编辑于:
贡献者: levy
+ diff --git a/frontend/performance-optimization-in-action.html b/frontend/performance-optimization-in-action.html index 23eb347c..d3293c29 100644 --- a/frontend/performance-optimization-in-action.html +++ b/frontend/performance-optimization-in-action.html @@ -5,7 +5,7 @@ - 前端项目性能优化实战 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

前端项目性能优化实战

Frontend

前端项目性能优化实战

本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。

检测

使用两个工具分析项目首页性能情况:

  1. https://developers.google.com/speed/pagespeed/insights/open in new window
  2. https://tools.pingdom.com/open in new window

得到结果如下:


可以看到,首页超过50%的请求都与图片有空,优化空间比较大,因此第一步应该是优化图片加载。

图片优化

关于图片,PageSpeed 的优化建议如下:

根据文章《把图片优化指南做成一个组件:v-imgopen in new window》,找到首页的图片相关的代码:

  1. <img>  元素修改成 <v-img/> ,注意设置 width 或 height
  2. <div ``style="background-image: url(img-url)"``></div> 修改成 <div v-img="{src: img-url}"></div>

注意:img-url 应该是 oss 的链接,并且是 https 协议。
如果是 http 协议,或不指定协议 //img-url,则很可能会出现下图的情况:

如果图片是放在项目中,且项目并没有部署到 oss,则无法享受自动加载 webp 格式图片的福利。

则在此环节,一次性做到了上图中的三个优化点。

提高TTFB时间

来看下一条优化建议:

因为项目是服务端渲染的,有些请求是在服务端做了。找到相关代码:

经过分析,上述代码存在两个问题:

  1. 可在客户端执行却放在了服务端
  2. 可并行执行却写成了串行

修改如下:


通过分析请求日志发现,有一个请求应该是在客户端发送,代码本意也是在客户端执行,却在服务端也执行了。
找到发送请求的代码:

原来是代码写在了 created 里,这是个经典的案例:为了让请求更早一点发送,不写在 mounted 钩子,而写在 created 里,导致请求分别在 server-side 与 client-side 都执行了。具体说明请看 vue ssr 官方文档open in new window

修改如下:

移除未使用的 Javascript

来看下一条优化建议:

因为经过多次迭代,有可能某些功能曾经上线过,后来被下线,但当时代码没删干净,所以留下一些现在没用的第三方库。根据建议,找到这些引入的 js,确保不影响正常功能后,删除即可。

此时注意用到以下基本操作:

  1. google/github 查询库的用途
  2. git history/annotate 查看是何人何时引入、并在何处使用的,如何代码不能表明意图,则最好找到相关人员询问是有何意图。

延迟静态资源的加载

来看另一个相关的建议:

如果有些第三方 js 确实有用到,但却不是关键资源,则可以延迟其加载或解析时机,缩短阻塞时间。

以一些第三方代码为例,它们并不是关键资源,可以在 window.onload 事件触发后,再加载

const srcs = [
+    
跳至主要內容

前端项目性能优化实战

Frontend

前端项目性能优化实战

本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。

检测

使用两个工具分析项目首页性能情况:

  1. https://developers.google.com/speed/pagespeed/insights/open in new window
  2. https://tools.pingdom.com/open in new window

得到结果如下:


可以看到,首页超过50%的请求都与图片有空,优化空间比较大,因此第一步应该是优化图片加载。

图片优化

关于图片,PageSpeed 的优化建议如下:

根据文章《把图片优化指南做成一个组件:v-imgopen in new window》,找到首页的图片相关的代码:

  1. <img>  元素修改成 <v-img/> ,注意设置 width 或 height
  2. <div ``style="background-image: url(img-url)"``></div> 修改成 <div v-img="{src: img-url}"></div>

注意:img-url 应该是 oss 的链接,并且是 https 协议。
如果是 http 协议,或不指定协议 //img-url,则很可能会出现下图的情况:

如果图片是放在项目中,且项目并没有部署到 oss,则无法享受自动加载 webp 格式图片的福利。

则在此环节,一次性做到了上图中的三个优化点。

提高TTFB时间

来看下一条优化建议:

因为项目是服务端渲染的,有些请求是在服务端做了。找到相关代码:

经过分析,上述代码存在两个问题:

  1. 可在客户端执行却放在了服务端
  2. 可并行执行却写成了串行

修改如下:


通过分析请求日志发现,有一个请求应该是在客户端发送,代码本意也是在客户端执行,却在服务端也执行了。
找到发送请求的代码:

原来是代码写在了 created 里,这是个经典的案例:为了让请求更早一点发送,不写在 mounted 钩子,而写在 created 里,导致请求分别在 server-side 与 client-side 都执行了。具体说明请看 vue ssr 官方文档open in new window

修改如下:

移除未使用的 Javascript

来看下一条优化建议:

因为经过多次迭代,有可能某些功能曾经上线过,后来被下线,但当时代码没删干净,所以留下一些现在没用的第三方库。根据建议,找到这些引入的 js,确保不影响正常功能后,删除即可。

此时注意用到以下基本操作:

  1. google/github 查询库的用途
  2. git history/annotate 查看是何人何时引入、并在何处使用的,如何代码不能表明意图,则最好找到相关人员询问是有何意图。

延迟静态资源的加载

来看另一个相关的建议:

如果有些第三方 js 确实有用到,但却不是关键资源,则可以延迟其加载或解析时机,缩短阻塞时间。

以一些第三方代码为例,它们并不是关键资源,可以在 window.onload 事件触发后,再加载

const srcs = [
   // 百度统计
   {
     src: 'https://hm.baidu.com/hm.js',
@@ -68,6 +68,6 @@
 

以阿里云 oss 为例进行说明,其他静态资源存储如 obs、S3 都是同理。

对于少量的资源,可以进行手工操作。打开 oss-browseropen in new window,找到相应资源:

本中使用的 oss-browser 版本,一次只能对一个资源进行 HTTP 头的设置,操作十分不便,可以登录阿里云控制台进行批量操作open in new window

当然,最根本的解决办法,是使用 阿里云oss命令行工具open in new window 上传的时候就进行设置。

/user/ossutil64 cp -r -f -u ./dist oss://$bucket/$file_path/ --meta=Cache-Control:max-age=31536000
 /user/ossutil64 set-meta oss://$bucket/$file_path/index.html Cache-Control:no-cache --update
 

注意,在命令行里别乱设置 Content-Encoding:gzip,否则会出现下面的情况,页面都打不开,具体说明查看详情open in new window

上次编辑于:
贡献者: levy
- + diff --git a/git/git-best-pratices.html b/git/git-best-pratices.html index 5e2bf89d..dfe8c7f7 100644 --- a/git/git-best-pratices.html +++ b/git/git-best-pratices.html @@ -5,7 +5,7 @@ - Git最佳实践 | levy @@ -34,13 +34,13 @@ } - + -
跳至主要內容

Git最佳实践

Git

2020-09-21

Git最佳实践

精简提交

一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。
如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。

频繁提交

一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。
经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。

此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。

不要提交不完整的改动

虽然原则上来说不要提交一些还没有完成的改动,但是对于一个非常庞大的新功能来说,也并不意味着你必须整体完成这个功能后才可以提交。恰恰相反,你必须把那些改动正确地分割成一些有意义的逻辑模块来进行频繁地提交。

如果你仅仅是因为急着想要下班,或者是想要得到一个干净的工作副本(比如想要切换到另一个分支上),你可以利用 Git 所提供的储藏(Stash)功能来解决这些问题。切记不要把那些不完整的改动提交到仓库中。

提交前测试那些改动

不要理所当然地认为自己完成的改动都是正确的。所有的改动一定要通过彻底地测试才表示它真正地被完成了。

版本控制不是备份系统

版本控制系统具有一个很强大的附带功能,那就是服务器端的备份功能。但是千万不要把 VCS 仅仅当成一个备份系统。特别需要注意的是,只能提交那些有意义的改动。

Github实例

一个功能对应一个分支

下面是好的示例: 格式化代码,也应该单独一个PR
下面是不好的示例:因为一个PR修改了不同的主题内容

提交“瘦”的PR

参考文章:https://deliveroo.engineering/2017/09/06/play-pull-request-roulette.html#ideas-to-make-your-prs-more-review-friendlyopen in new window
其中最重要的一点:不要一次提交一个很大改动的PR,否则别人很难 review,要学会拆分步骤。
下面是一个 PR 示例:
拆分前,包含了35个改动,很难 review

下图是拆分后:

单个PR的改动文件只有11个
每个 PR 改动的文件少了,这样 review 起来就更容易了。

使用正确的标题

相关规范看这里open in new window

另外,请回答:出于什么原因需要进行这次修改?具体改动了些什么?

  • 使用一定要使用现在时祈使句(例如要使用 change ,而不是 changed 或 changes)。
  • 优先使用正面肯定语句,而不是否定句。

好的示例:docs: extraQuery 的正确使用方法
不好的示例:docs: 更新不直观的例子

根据模板填写PR描述

这是我们 Github 的 PR 模板,融合了我们的最佳实践
下面是实际的好的例子

自动关闭issue

image.png
image.png
git commit -m 'fix #6'
+    
跳至主要內容

Git最佳实践

Git

2020-09-21

Git最佳实践

精简提交

一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。
如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。

频繁提交

一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。
经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。

此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。

不要提交不完整的改动

虽然原则上来说不要提交一些还没有完成的改动,但是对于一个非常庞大的新功能来说,也并不意味着你必须整体完成这个功能后才可以提交。恰恰相反,你必须把那些改动正确地分割成一些有意义的逻辑模块来进行频繁地提交。

如果你仅仅是因为急着想要下班,或者是想要得到一个干净的工作副本(比如想要切换到另一个分支上),你可以利用 Git 所提供的储藏(Stash)功能来解决这些问题。切记不要把那些不完整的改动提交到仓库中。

提交前测试那些改动

不要理所当然地认为自己完成的改动都是正确的。所有的改动一定要通过彻底地测试才表示它真正地被完成了。

版本控制不是备份系统

版本控制系统具有一个很强大的附带功能,那就是服务器端的备份功能。但是千万不要把 VCS 仅仅当成一个备份系统。特别需要注意的是,只能提交那些有意义的改动。

Github实例

一个功能对应一个分支

下面是好的示例: 格式化代码,也应该单独一个PR
下面是不好的示例:因为一个PR修改了不同的主题内容

提交“瘦”的PR

参考文章:https://deliveroo.engineering/2017/09/06/play-pull-request-roulette.html#ideas-to-make-your-prs-more-review-friendlyopen in new window
其中最重要的一点:不要一次提交一个很大改动的PR,否则别人很难 review,要学会拆分步骤。
下面是一个 PR 示例:
拆分前,包含了35个改动,很难 review

下图是拆分后:

单个PR的改动文件只有11个
每个 PR 改动的文件少了,这样 review 起来就更容易了。

使用正确的标题

相关规范看这里open in new window

另外,请回答:出于什么原因需要进行这次修改?具体改动了些什么?

  • 使用一定要使用现在时祈使句(例如要使用 change ,而不是 changed 或 changes)。
  • 优先使用正面肯定语句,而不是否定句。

好的示例:docs: extraQuery 的正确使用方法
不好的示例:docs: 更新不直观的例子

根据模板填写PR描述

这是我们 Github 的 PR 模板,融合了我们的最佳实践
下面是实际的好的例子

自动关闭issue

image.png
image.png
git commit -m 'fix #6'
 # 或
 git commit -m 'close #6'
 

当pr合并时,将自动close issue

1+2 review 规则

1 是指发起 PR 的人,2 是指进行 code review 的人。也即,每一个 PR,至少要经过两个团队成员 approve 才能合并。

上面是针对 github 的协作,项目组中可酌情变为 1+1 规则


礼貌提问

在 github 向人提问时,需要有礼貌。当提出 feature request时,还要说明自己的情况,尽可能提供更多的信息给对方。

上面的示例有三个重点:

  1. 开头表达感谢
  2. 中间说明己方的使用情况,并给出相应链接
  3. 最后参考业界已有实现,给出一个方案设想,并给出相应链接

学习资源

上次编辑于:
贡献者: levy
- + diff --git a/git/git-definitive-guide-to-merge-code.html b/git/git-definitive-guide-to-merge-code.html index 6271fc39..4096b313 100644 --- a/git/git-definitive-guide-to-merge-code.html +++ b/git/git-definitive-guide-to-merge-code.html @@ -5,7 +5,7 @@ - Git代码合并指南 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

Git代码合并指南

Git

Git代码合并指南

前言

合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。
在说明问题前,先定义一些概念:

  • feat:指代功能分支
  • dev 与 test:指代两条不同的长驻分支,它们具有以下特点:
    • 受保护,不能直接推送
    • 不会被删除
    • 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test
  • MR:merge request。代码合并请求

以及说明本文解决冲突涉及到的工具及平台:

  • 使用 IDEA 解决冲突(JetBrains系列的工具都适用)
  • 使用 GitLab 托管代码

功能分支合并长驻分支冲突

这是最常见的场景:feat1 与 feat2 并行开发,当提交MR( feat1 -> dev )时,发现冲突了,无法合并。
下面先给出解决思路,再给出图文操作步骤。

解决思路

  1. 因为合并的方向是 feat -> dev,所以解决冲突应该是在本地 dev 合并 feat
  2. 又因为本地 dev 不能向远程推送,因而需要基于 dev 切一个新分支 conflict/resolved
  3. 推送 conflict/resolved 分支
  4. 提交 MR:conflict/resolved -> dev

操作步骤

  1. 本地切换到 dev 分支,更新代码
  2. 合并相应的 feat 分支
  1. 弹出冲突提示,点击合并
  1. 首先处理无冲突的代码,点击下图红框处
  1. 再根据情况,选择合并代码或丢弃代码。
  1. 在 dev 分支上切出新分支,推荐命名为 conflict/xxx
  1. 推送代码,提交MR(conflict -> dev),记得勾选合并后删除

功能分支被污染

分支一多,人难免失误,很可能造成 feat 分支被污染,即当提MR(feat -> test)时,出现不想合并到 test的代码或提交记录。
这种场景的出现可能有多种原因:

  1. 研发过程中出现误操作,如出现了 dev -> feat 的合并
  2. feat 分支的基线分支搞错了,如从 dev 切出了 feat

解决思路

  1. 基于目标分支如 (test 分支)切一个干净的分支 clean
  2. 使用 cherry-pick,挑选自己想要的提交
  3. 再提交MR(clean -> test)

注意的是,要按提交顺序进行 cherry-pick,以避免遗漏或出错。

操作步骤

  1. 更新目标分支(在这里是 test)
  1. 基于 test 切新分支,这里示例命名为:clean
  1. 在拥有最新代码的分支(这里是 feat) 找到并选中相应的提交记录
  2. 右键,点击 Cherry-Pick
  3. 则相应的提交记录就会合并到 clean 分支
  4. 推送 clean,提交MR(clean -> test)

挑选别的分支部分代码合并

有可能会出现这样一种场景:

  • 最新的生产代码里,假设版本为v1.3.0,包含了 feat 分支的代码
  • 为了减少分支的冗余,代码一旦上生产后,就会清除相应的功能分支,也即此时仓库里没有 feat 分支了
  • 客户方部署的版本代码为 v1.1.0,而客户不想升级到最新的版本,只想要 feat 分支相应的功能

此时该如何是好?

解决思路

其实只要触发“挑选”关键字,就可以考虑使用 cherry-pick。
feat 分支就算被删了,只要提交记录还在,那也没关系:

  • 在v1.3.0 的代码库中,按分支筛选,找出 feat 分支对应的提交记录
  • 通过 cherry-pick 把 feat 分支的代码合并到客户方的代码分支即可

注意:毕竟跨越了版本,无法保证合并过去后的代码一定能正确工作,需要进行充分地测试。

操作步骤

此操作本质还是 cherry-pick,参考前面 cherry-pick 的示例即可。

上次编辑于:
贡献者: levy
- +
跳至主要內容

Git代码合并指南

Git

Git代码合并指南

前言

合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。
在说明问题前,先定义一些概念:

  • feat:指代功能分支
  • dev 与 test:指代两条不同的长驻分支,它们具有以下特点:
    • 受保护,不能直接推送
    • 不会被删除
    • 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test
  • MR:merge request。代码合并请求

以及说明本文解决冲突涉及到的工具及平台:

  • 使用 IDEA 解决冲突(JetBrains系列的工具都适用)
  • 使用 GitLab 托管代码

功能分支合并长驻分支冲突

这是最常见的场景:feat1 与 feat2 并行开发,当提交MR( feat1 -> dev )时,发现冲突了,无法合并。
下面先给出解决思路,再给出图文操作步骤。

解决思路

  1. 因为合并的方向是 feat -> dev,所以解决冲突应该是在本地 dev 合并 feat
  2. 又因为本地 dev 不能向远程推送,因而需要基于 dev 切一个新分支 conflict/resolved
  3. 推送 conflict/resolved 分支
  4. 提交 MR:conflict/resolved -> dev

操作步骤

  1. 本地切换到 dev 分支,更新代码
  2. 合并相应的 feat 分支
  1. 弹出冲突提示,点击合并
  1. 首先处理无冲突的代码,点击下图红框处
  1. 再根据情况,选择合并代码或丢弃代码。
  1. 在 dev 分支上切出新分支,推荐命名为 conflict/xxx
  1. 推送代码,提交MR(conflict -> dev),记得勾选合并后删除

功能分支被污染

分支一多,人难免失误,很可能造成 feat 分支被污染,即当提MR(feat -> test)时,出现不想合并到 test的代码或提交记录。
这种场景的出现可能有多种原因:

  1. 研发过程中出现误操作,如出现了 dev -> feat 的合并
  2. feat 分支的基线分支搞错了,如从 dev 切出了 feat

解决思路

  1. 基于目标分支如 (test 分支)切一个干净的分支 clean
  2. 使用 cherry-pick,挑选自己想要的提交
  3. 再提交MR(clean -> test)

注意的是,要按提交顺序进行 cherry-pick,以避免遗漏或出错。

操作步骤

  1. 更新目标分支(在这里是 test)
  1. 基于 test 切新分支,这里示例命名为:clean
  1. 在拥有最新代码的分支(这里是 feat) 找到并选中相应的提交记录
  2. 右键,点击 Cherry-Pick
  3. 则相应的提交记录就会合并到 clean 分支
  4. 推送 clean,提交MR(clean -> test)

挑选别的分支部分代码合并

有可能会出现这样一种场景:

  • 最新的生产代码里,假设版本为v1.3.0,包含了 feat 分支的代码
  • 为了减少分支的冗余,代码一旦上生产后,就会清除相应的功能分支,也即此时仓库里没有 feat 分支了
  • 客户方部署的版本代码为 v1.1.0,而客户不想升级到最新的版本,只想要 feat 分支相应的功能

此时该如何是好?

解决思路

其实只要触发“挑选”关键字,就可以考虑使用 cherry-pick。
feat 分支就算被删了,只要提交记录还在,那也没关系:

  • 在v1.3.0 的代码库中,按分支筛选,找出 feat 分支对应的提交记录
  • 通过 cherry-pick 把 feat 分支的代码合并到客户方的代码分支即可

注意:毕竟跨越了版本,无法保证合并过去后的代码一定能正确工作,需要进行充分地测试。

操作步骤

此操作本质还是 cherry-pick,参考前面 cherry-pick 的示例即可。

上次编辑于:
贡献者: levy
+ diff --git a/git/git-history-two-tricks-in-idea.html b/git/git-history-two-tricks-in-idea.html index e8478249..9eadb26b 100644 --- a/git/git-history-two-tricks-in-idea.html +++ b/git/git-history-two-tricks-in-idea.html @@ -5,7 +5,7 @@ - Git查看历史记录小技巧 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

Git查看历史记录小技巧

Git

Git查看历史记录小技巧

分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。

这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。

第一个是叫 annotate with git blame
在IDEA的行号这个位置,右键,再点击即可。如图所示:

效果就是,每一行代码都会显示,该行代码是由谁提交的、 什么时候提交的。

在合并冲突的时候也可以用这个技巧。

对左右两边进行git blame一下, 然后就可以看到如图所示的情况:

这样就能提供更多的信息帮助解决冲突。

就算冲突无法自己解决,也至少能知道提交代码的是谁,可以找到作者去进行沟通。

当然git blame 是有一些注意点的。因为它本质上显示的是某一行代码的最后提交人, 也就是last modified的一个概念,而有些时候这并不意味着最后的修改人就是代码的原作者。

之所以这样,可能会有以下的原因:

  1. 代码格式化
  2. 移动代码,比如说拷贝代码、迁移代码
  3. 合并代码,解决冲突

上述操作都会改变最后修改人的这个属性,但此时显然最后修改人并非原作者。

这说明,有时候仅知道了某一行代码是由谁最近修改的还不够,还需要知道某一个文件经过了怎么样的修改。

这就引出了第二个小技巧了: git show history

点击IDEA某个文件的空白处,然后右键,选择git,然后点击 show history。

就会出现如图所示的这样的一个 git log的 界面。

那么这样就可以看到这个文件从最初到至今经历过了怎样的修改、 有过哪些人在上面修改, 从而更好的进行记录追踪。

这两个技巧, 其实是越有经验就对你帮助越大的。因为你,年限越长,你看别人的代码的机会就越; 而如果你年限尚浅的话,更多的是你的代码被别人 review。

上次编辑于:
贡献者: levy
- +
跳至主要內容

Git查看历史记录小技巧

Git

Git查看历史记录小技巧

分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。

这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。

第一个是叫 annotate with git blame
在IDEA的行号这个位置,右键,再点击即可。如图所示:

效果就是,每一行代码都会显示,该行代码是由谁提交的、 什么时候提交的。

在合并冲突的时候也可以用这个技巧。

对左右两边进行git blame一下, 然后就可以看到如图所示的情况:

这样就能提供更多的信息帮助解决冲突。

就算冲突无法自己解决,也至少能知道提交代码的是谁,可以找到作者去进行沟通。

当然git blame 是有一些注意点的。因为它本质上显示的是某一行代码的最后提交人, 也就是last modified的一个概念,而有些时候这并不意味着最后的修改人就是代码的原作者。

之所以这样,可能会有以下的原因:

  1. 代码格式化
  2. 移动代码,比如说拷贝代码、迁移代码
  3. 合并代码,解决冲突

上述操作都会改变最后修改人的这个属性,但此时显然最后修改人并非原作者。

这说明,有时候仅知道了某一行代码是由谁最近修改的还不够,还需要知道某一个文件经过了怎么样的修改。

这就引出了第二个小技巧了: git show history

点击IDEA某个文件的空白处,然后右键,选择git,然后点击 show history。

就会出现如图所示的这样的一个 git log的 界面。

那么这样就可以看到这个文件从最初到至今经历过了怎样的修改、 有过哪些人在上面修改, 从而更好的进行记录追踪。

这两个技巧, 其实是越有经验就对你帮助越大的。因为你,年限越长,你看别人的代码的机会就越; 而如果你年限尚浅的话,更多的是你的代码被别人 review。

上次编辑于:
贡献者: levy
+ diff --git a/git/git-useful-commands.html b/git/git-useful-commands.html index 18e207f4..a8aa2293 100644 --- a/git/git-useful-commands.html +++ b/git/git-useful-commands.html @@ -5,7 +5,7 @@ - Git常用命令 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

Git常用命令

Git

Git常用命令

前言

本文将列举Git常见场景,并给出相应解决方案。

约定: 下文代码块中${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。

推荐: 图形化交互式Git教程open in new window

配置

Mac/Linux 用户 执行以下操作

vi ~/.gitconfig
+    
跳至主要內容

Git常用命令

Git

Git常用命令

前言

本文将列举Git常见场景,并给出相应解决方案。

约定: 下文代码块中${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。

推荐: 图形化交互式Git教程open in new window

配置

Mac/Linux 用户 执行以下操作

vi ~/.gitconfig
 

Windows用户在桌面用户文件夹下有个.gitconfig隐藏文件,直接修改即可

补充以下内容

[alias]
   st = status
   cm = commit
@@ -138,6 +138,6 @@
 

从所有提交中删除一个文件

git filter-branch --tree-filter "rm -rf package-lock.json" --prune-empty -- --all
 

如果代码已经推送到了远程仓库,还需要强制推送

git push -f
 
上次编辑于:
贡献者: levy
- + diff --git a/git/gitlab-ci.html b/git/gitlab-ci.html index 6f15ffb7..8e429d50 100644 --- a/git/gitlab-ci.html +++ b/git/gitlab-ci.html @@ -5,7 +5,7 @@ - GitLab CI | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

GitLab CI

GitGitLabJavaNode.js

GitLab CI

前言

GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。

安装与配置

GitLab Runner 安装

进行 Gitlab CI 的第一步是要安装 GitLab Runner。如果公司、团队内部已安装过,可以跳过这一步。

这里推荐使用 docker 的方式安装,复制以下命令执行即可:

docker run -d --name gitlab-runner --restart always \
+    
跳至主要內容

GitLab CI

GitGitLabJavaNode.js

GitLab CI

前言

GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。

安装与配置

GitLab Runner 安装

进行 Gitlab CI 的第一步是要安装 GitLab Runner。如果公司、团队内部已安装过,可以跳过这一步。

这里推荐使用 docker 的方式安装,复制以下命令执行即可:

docker run -d --name gitlab-runner --restart always \
   -v /var/run/docker.sock:/var/run/docker.sock \
   -v /srv/gitlab-runner/config:/etc/gitlab-runner \
   gitlab/gitlab-runner:latest
@@ -147,6 +147,6 @@
 sudo systemctl status docker
 

本地成功,流水线失败

如果流水线编译报错,本地编译通过,不用怀疑,一定是本地的问题。

本地之所以能编译通过,是因为有缓存。如果 pom.xml 没有设置 <updatePolicy>always</updatePolicy>,编译时很可能使用的是缓存。

清除缓存拉取最新的包即可。

mvn -U clean install
 

参考文档

上次编辑于:
贡献者: levy
- + diff --git a/git/index.html b/git/index.html index 1b10a814..2688fa99 100644 --- a/git/index.html +++ b/git/index.html @@ -34,10 +34,10 @@ } - + - - + + diff --git a/git/rethinking-git-flow.html b/git/rethinking-git-flow.html index 3ca855a8..360cd29a 100644 --- a/git/rethinking-git-flow.html +++ b/git/rethinking-git-flow.html @@ -5,7 +5,7 @@ - 再论Git Flow | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

再论Git Flow

Git

再论Git Flow

背景

团队目前使用的 Git 协作模式是:

  1. 对每个功能建立相应的 feat 分支
  2. 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat
  3. 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支

这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:

  1. 某 feat 合并至 dev 后,并不想合并至 test
  2. 某 feat 合并至 test 后,并不想合并至 uat

本文暂且不讨论该交付理念的优劣,毕竟每个团队研发情况、交付理念都不一样。 本文关注的是,在满足上述需求的情况下,是否有更好的分支协作方式。

动机

为什么要寻求更好的方式?因为上述分支协作模式,会导致代码冲突的噩梦:

  1. feat -> dev,解决冲突
  2. feat -> test,又要解决冲突
  3. feat -> uat,还要解决冲突

正如此文章open in new window所说,“把时间浪费在解决不必要的冲突上”。

再者,功能已经通过测试了,准备上UAT环境时,居然还要解决一大堆曾经解决过的冲突,实在不想接受这种“惊喜”(或者说“惊吓”更合适)——合错代码了怎么办?并且,这么多分支,遗漏了怎么办?这些问题可以解决,但难免有为了解决一个问题,引入更多问题之嫌。

理想中的研发流程是,测试通过后,上 UAT 的体验是平滑的,是不用担心出错的。

为此,本文思考是否存在另一种分支协作的方式。

分析

首先分析一下,“某功能测试通过但不上 UAT”的可操作方法有哪些:

  1. 要上 UAT 的 feat 分支逐个依次合并至长驻分支,也即当前的做法
  2. 在原计划要上的功能的代码集合中,剔除掉相应 feat 的代码,再上 UAT
  3. 相应分支再次提交代码,或提交 revert commit,或屏蔽相应的功能及入口,变相达到目的

剔除代码

先来看第2种方法。filter by branch,这是最先想到且符合直觉的方式,可惜实际上 Git 并没有此功能。

想“剔除某 feat 分支的代码”,可操作方式如下,更多请参考此文章open in new window

  1. git rebase
  2. git cherry-pick

git rebase要求 commit 是连续的,这对于实际不可行,因为集成分支里各个 feat 分支的提交记录掺杂在一起。

git cherry-pick是可行的。不过其思路是挑捡想要的 commit,放到目标分支,本质上并不是剔除的逻辑。

再次提交

真正的剔除逻辑,存在于第3种方法中。提交一个 revert commit,就可以把之前的代码干掉了(如果想恢复代码,需要 revert "revert commit")。

觉得 revert 可能会对后续恢复代码造成困扰的话,也可以再提交代码,屏蔽相应功能及入口。这种方式适合于功能入口少,功能本身具有类似开关特性的场景。

比较优劣

要比较上述方案优劣,本文倾向于使用功利主义的最佳实践作为指导思想——认为痛苦存在更多共同点,因而为避免消极而努力。换言之,本文关注的是,哪种方案最令人痛苦,则优先淘汰它。

还有一个指导思想:麻烦、辛苦的事情放前面;前面可以多做,后面期望少做。

当前的方式,存在最难受的问题:解决过的冲突,需要重复地解决。涉及范围:全部分支。涉及人员:所有参与研发的人员,即使他们在别的 feat 分支提交代码。

cherry-pick 依然存在要重复解决冲突的问题,且涉及范围同样为全部分支,但涉及人员减少为单人,因为只需要一个做 cherry-pick 的工作,由其解决 cherry-pick 遇到的冲突(当然,很可能需要他人协助)。

再次提交,冲突的可能性将大大减少,涉及范围:相应的 feat 分支。涉及人员:相应的 feat 分支研发人员。

也即使用再次提交的方案,痛苦将降低至最小。这也是符合直觉的:谁出问题,谁负责。某功能不上线了,这也算是“问题”的一种,则相应的负责人去处理,尽可能不影响到其他人。

至于再次提交是使用 revert 还是屏蔽功能及入口,则具体情况具体分析。

实例

下面举例说明,如何应用上述分析结果。

分支模型

长驻分支: dev/test/uat,分别对应环境:研发/测试/UAT

一个月一次的迭代开始时,都建立相应的  release 分支,命名规则可以:

  • 按版本,如: release/v2.15
  • 按上线日期,如:release/04-26

功能提交

每个研发人员根据相应功能,从 uat checkout 相应的 feat 分支。

每次需要集成发布时,正常的分支合并操作如下:

  1. feat -> dev
  2. feat -> release
  3. release -> test
  4. release -> uat

则冲突大多数情况只发生在第前两步,解决之后,后续上测试环境、上 UAT 环境,基本无需担心冲突。

为什么一个 feat 要合并两次?

因为要保证 release 的功能是较为完整的, 至少经过开发人员在 dev 环境的自测。

并且这样也能适应不同的功能分批提测的研发节奏。

功能回撤

当 release 合并至 test 分支后,得到通知,某功能(分支涉及 feat/unwanted)不上 UAT。

则此时,feat/unwanted 相应的研发人员,为了进行功能回滚,操作如下:

  1. checkout rollback 分支
  2. 进行回滚提交,或 revert,或屏蔽功能入口
  3. 请求合并至 UAT(不合并至 release/分支):

后续要恢复功能,在 rollback 分支操作,再合并至 UAT 即可。

结论

通过分析与比较,本文推荐使用“再次提交”的方式,来满足某 feat 分支合进 test,不合进 uat 分支的需求。

这样做,将改动涉及范围减至最小,涉及人员降为单人,大大减少上 UAT 时合并代码的痛苦,达到平稳上线的目的。

上次编辑于:
贡献者: levy
- +
跳至主要內容

再论Git Flow

Git

再论Git Flow

背景

团队目前使用的 Git 协作模式是:

  1. 对每个功能建立相应的 feat 分支
  2. 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat
  3. 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支

这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:

  1. 某 feat 合并至 dev 后,并不想合并至 test
  2. 某 feat 合并至 test 后,并不想合并至 uat

本文暂且不讨论该交付理念的优劣,毕竟每个团队研发情况、交付理念都不一样。 本文关注的是,在满足上述需求的情况下,是否有更好的分支协作方式。

动机

为什么要寻求更好的方式?因为上述分支协作模式,会导致代码冲突的噩梦:

  1. feat -> dev,解决冲突
  2. feat -> test,又要解决冲突
  3. feat -> uat,还要解决冲突

正如此文章open in new window所说,“把时间浪费在解决不必要的冲突上”。

再者,功能已经通过测试了,准备上UAT环境时,居然还要解决一大堆曾经解决过的冲突,实在不想接受这种“惊喜”(或者说“惊吓”更合适)——合错代码了怎么办?并且,这么多分支,遗漏了怎么办?这些问题可以解决,但难免有为了解决一个问题,引入更多问题之嫌。

理想中的研发流程是,测试通过后,上 UAT 的体验是平滑的,是不用担心出错的。

为此,本文思考是否存在另一种分支协作的方式。

分析

首先分析一下,“某功能测试通过但不上 UAT”的可操作方法有哪些:

  1. 要上 UAT 的 feat 分支逐个依次合并至长驻分支,也即当前的做法
  2. 在原计划要上的功能的代码集合中,剔除掉相应 feat 的代码,再上 UAT
  3. 相应分支再次提交代码,或提交 revert commit,或屏蔽相应的功能及入口,变相达到目的

剔除代码

先来看第2种方法。filter by branch,这是最先想到且符合直觉的方式,可惜实际上 Git 并没有此功能。

想“剔除某 feat 分支的代码”,可操作方式如下,更多请参考此文章open in new window

  1. git rebase
  2. git cherry-pick

git rebase要求 commit 是连续的,这对于实际不可行,因为集成分支里各个 feat 分支的提交记录掺杂在一起。

git cherry-pick是可行的。不过其思路是挑捡想要的 commit,放到目标分支,本质上并不是剔除的逻辑。

再次提交

真正的剔除逻辑,存在于第3种方法中。提交一个 revert commit,就可以把之前的代码干掉了(如果想恢复代码,需要 revert "revert commit")。

觉得 revert 可能会对后续恢复代码造成困扰的话,也可以再提交代码,屏蔽相应功能及入口。这种方式适合于功能入口少,功能本身具有类似开关特性的场景。

比较优劣

要比较上述方案优劣,本文倾向于使用功利主义的最佳实践作为指导思想——认为痛苦存在更多共同点,因而为避免消极而努力。换言之,本文关注的是,哪种方案最令人痛苦,则优先淘汰它。

还有一个指导思想:麻烦、辛苦的事情放前面;前面可以多做,后面期望少做。

当前的方式,存在最难受的问题:解决过的冲突,需要重复地解决。涉及范围:全部分支。涉及人员:所有参与研发的人员,即使他们在别的 feat 分支提交代码。

cherry-pick 依然存在要重复解决冲突的问题,且涉及范围同样为全部分支,但涉及人员减少为单人,因为只需要一个做 cherry-pick 的工作,由其解决 cherry-pick 遇到的冲突(当然,很可能需要他人协助)。

再次提交,冲突的可能性将大大减少,涉及范围:相应的 feat 分支。涉及人员:相应的 feat 分支研发人员。

也即使用再次提交的方案,痛苦将降低至最小。这也是符合直觉的:谁出问题,谁负责。某功能不上线了,这也算是“问题”的一种,则相应的负责人去处理,尽可能不影响到其他人。

至于再次提交是使用 revert 还是屏蔽功能及入口,则具体情况具体分析。

实例

下面举例说明,如何应用上述分析结果。

分支模型

长驻分支: dev/test/uat,分别对应环境:研发/测试/UAT

一个月一次的迭代开始时,都建立相应的  release 分支,命名规则可以:

  • 按版本,如: release/v2.15
  • 按上线日期,如:release/04-26

功能提交

每个研发人员根据相应功能,从 uat checkout 相应的 feat 分支。

每次需要集成发布时,正常的分支合并操作如下:

  1. feat -> dev
  2. feat -> release
  3. release -> test
  4. release -> uat

则冲突大多数情况只发生在第前两步,解决之后,后续上测试环境、上 UAT 环境,基本无需担心冲突。

为什么一个 feat 要合并两次?

因为要保证 release 的功能是较为完整的, 至少经过开发人员在 dev 环境的自测。

并且这样也能适应不同的功能分批提测的研发节奏。

功能回撤

当 release 合并至 test 分支后,得到通知,某功能(分支涉及 feat/unwanted)不上 UAT。

则此时,feat/unwanted 相应的研发人员,为了进行功能回滚,操作如下:

  1. checkout rollback 分支
  2. 进行回滚提交,或 revert,或屏蔽功能入口
  3. 请求合并至 UAT(不合并至 release/分支):

后续要恢复功能,在 rollback 分支操作,再合并至 UAT 即可。

结论

通过分析与比较,本文推荐使用“再次提交”的方式,来满足某 feat 分支合进 test,不合进 uat 分支的需求。

这样做,将改动涉及范围减至最小,涉及人员降为单人,大大减少上 UAT 时合并代码的痛苦,达到平稳上线的目的。

上次编辑于:
贡献者: levy
+ diff --git a/git/use-command-line-tool-to-manage-gitlab-merge-request.html b/git/use-command-line-tool-to-manage-gitlab-merge-request.html index 457f7579..73135d92 100644 --- a/git/use-command-line-tool-to-manage-gitlab-merge-request.html +++ b/git/use-command-line-tool-to-manage-gitlab-merge-request.html @@ -5,7 +5,7 @@ - 操作 Gitlab MR 的命令行工具 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

操作 Gitlab MR 的命令行工具

GitGitLabPython

操作 Gitlab MR 的命令行工具

背景

为什么开发这个工具?主要解决以下问题:

  1. 提测、上 UAT 时,避免漏合代码。
  2. 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?”
  3. 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。

可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。
并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。
对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。

安装

解压zip

下载并解压文件:

安装git bash

Windows系统才要安装。
如果 git bash 版本不足 2.41.0,最好安装最新版本。

安装地址:https://gitforwindows.org/open in new window

配置

新增文件

vi ~/.mr-config.json
+    
跳至主要內容

操作 Gitlab MR 的命令行工具

GitGitLabPython

操作 Gitlab MR 的命令行工具

背景

为什么开发这个工具?主要解决以下问题:

  1. 提测、上 UAT 时,避免漏合代码。
  2. 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?”
  3. 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。

可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。
并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。
对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。

安装

解压zip

下载并解压文件:

安装git bash

Windows系统才要安装。
如果 git bash 版本不足 2.41.0,最好安装最新版本。

安装地址:https://gitforwindows.org/open in new window

配置

新增文件

vi ~/.mr-config.json
 

复制以下内容:

{
   "gitlab_url":"https://your-gitlab.com",
   "gitlab_token":"your-token",
@@ -63,6 +63,6 @@
 

出现提示,是否自动切换分支为解决冲突作准备:
image.png
当然在此之前,要保证工作目录是干净的,如果有修改未提交,会中止切换分支操作:
image.png

可以使用 git stash保存修改,合并冲突后,再 git stash pop

命令执行成功时,会切换到 conflict/ 开头的分支。
此时,打开 IDE 或 Git 管理工具,根据提示把相应的分支合并到 conflict/ 分支即可。
image.png

以 IDEA 为例:
image.png

解决冲突后,再切回命令行,此时有两种选择:

  1. 创建 MR,适用于自己没有权限合并的场景
  2. 合并 MR,适用于自己有权限合并的场景

如果是创建,再次执行 create 命令即可:

mr create
 

image.png
创建的 MR 合并时会自动删除 conflict/ 分支。
image.png

如果是合并,同样再次执行 merge 命令即可,此时不用带参数:

mr merge
 

image.png
52fee749bc6d270f9ccab3eb0e04208b.png

上次编辑于:
贡献者: levy
- + diff --git a/index.html b/index.html index 4f8b4b47..9c185bd7 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ - levy's blog | levy @@ -34,10 +34,47 @@ } - + -
跳至主要內容

levy's blog

思考,表达,练习,创造

避免密码明文传输

避免密码明文传输

+
跳至主要內容

levy's blog

思考,表达,练习,创造

Boolean 还是 boolean?

Boolean 还是 boolean?

+

在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢?

+

结论

+

先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。

+

原文如下:
+image.png


levy大约 4 分钟
forEach 还是 map?

forEach 还是 map?

+

背景

+

遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有:

+
List<Type> result = new ArrayList<>();
+list.forEach(src -> {
+    Type target = BeanUtils.copyProperties(src, target);
+    //省略代码
+    result.add(target);
+});
+

levy大约 4 分钟
生产教训:测试环境要与生产环境一致

生产教训:测试环境要与生产环境一致

+

事件还原

+

业务流程:

+
    +
  1. app-a 上传文件
  2. +
  3. app-b 下载文件后使用文件
  4. +
+

其他信息:

+
    +
  1. 开发、测试环境使用 MinIO
  2. +
  3. 生产环境使用 Amazon S3
  4. +
+

问题:

+
    +
  1. app-a 上传文件成功
  2. +
  3. app-b 使用文件报错
  4. +
+

逐步分析定位问题:

+
    +
  1. app-a 与 app-b 配置是否一致?——确认都是使用 S3
  2. +
  3. S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题
  4. +
  5. app-a 是否真的上传成功?——确认文件已在 S3
  6. +
  7. app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。
  8. +

levy大约 2 分钟Daily
避免密码明文传输

避免密码明文传输

说明

密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。

本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。

@@ -61,34 +98,15 @@

前言

虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。

而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。

还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:

-

levy大约 5 分钟Node.jsDaily
根据时间范围查询推荐实践

根据时间范围查询推荐实践

背景

不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。

虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。

本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson


levy大约 3 分钟JavaDaily
微软中国CTO演讲观后感

微软中国CTO演讲观后感

看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。

-

levy小于 1 分钟DailyVideo
迭代复盘之三员管理

迭代复盘之三员管理

-

前言

-

本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。

-

这次迭代因为各种原因,延期了快一个星期(周六还加了班)。

-

那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。

-

levy大约 3 分钟DailyWorking Experience
Excel处理常用实践

Excel处理常用实践

-

基础知识

-

导入需要用到对象,MultipartFile。

-
@PostMapping("/import")
-public boolean importLicense(
-      @RequestParam("file") MultipartFile file,
-      @RequestParam("tenantId") @NotBlank String tenantId,
-) {
-  return true;
-}
-

levy大约 5 分钟JavaDaily
都什么年代了,还在用传统方式写代码?

都什么年代了,还在用传统方式写代码?

-

前言

-

还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

-

本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

-

本文将分享 AI 时代的编程新实践,教你如何从一个 "Ctrl + C"、 "Ctrl + V" 工程师,变成一个 "Tab + Enter" 工程师🤣。


levy大约 7 分钟AIDaily
2
3
4
5
6
- +

levy小于 1 分钟DailyVideo
2
3
4
5
...
7
+ diff --git a/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html b/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html index 9d750f56..ab2aea3c 100644 --- a/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html +++ b/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html @@ -5,7 +5,7 @@ - IDEA常见问题与解决方案 | levy @@ -34,16 +34,16 @@ } - + -
跳至主要內容

IDEA常见问题与解决方案

JavaDaily

IDEA常见问题与解决方案

启动参数过长

Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun.
解决方案:

  1. 编辑 .idea/workspace.xml
  2. 找到 PropertiesComponent
  3. 添加:

或者这样:
image.png

"dynamic.classpath": "true",
+    
跳至主要內容

IDEA常见问题与解决方案

JavaDaily

IDEA常见问题与解决方案

启动参数过长

Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun.
解决方案:

  1. 编辑 .idea/workspace.xml
  2. 找到 PropertiesComponent
  3. 添加:

或者这样:
image.png

"dynamic.classpath": "true",
 

设置JDK版本

相关报错:

解决方案如下。

1.先确保已安装 jdk。

2.修改运行设置
image.png
image.png
3.修改外部依赖设置
image.png
image.png

lombok 编译报错

前提:lombok 有maven依赖后,还要安装IDE插件open in new window

相关报错:
class lombok.javac.apt.LombokProcessor (in unnamed module @0x29f3e3c7) cannot access class com.sun.tools.javac.processing.JavacProcessingEnvironment (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.processing to unnamed module @0x29f3e3c7open in new window

解决方案:找到相应的 pom.xml,更新依赖版本(如果没有,则添加依赖)

<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <version>1.18.20</version>
 </dependency>
 

当然,还要确保项目 JDK 版本正确
open in new window

设置启动参数

Run -> Edit Configurations
注意是 VM options

注入环境变量:spring.profiles.active=local

也可以设置 VM options,不过要带上 -D:-Dspring.profiles.active=local

栈溢出

maven build "Exception in thread "main" java.lang.StackOverflowError"

-Xss40m
image.png

不是maven的编译选项在下面
image.png

内存不足

如图所示进入设置:image.png
-Xmx4011m


相关报错:
java: java.lang.OutOfMemoryError: GC overhead limit exceeded

解决方案:需要进行如图所示修改设置
image.png

热加载

相关文章:https://cloud.tencent.com/developer/article/1683029open in new window

提示:不用追求自动重新编译,手动按 build 即可。

终端加载环境变量

相关问答:https://stackoverflow.com/questions/36592226/bashrc-not-sourced-on-intellij-ideas-terminal/59138750#59138750open in new window

注意两点:

  1. shell 命令带上 -i
  2. 根据 shell 的版本,使用 .bashrc 或 .zshrc

添加外部jar作为依赖

如下图所示:
image.png
打开相应文件夹,选中jar即可。

文件找不到——依赖冲突

相关报错:nested exception is java.io.FileNotFoundException
这一般是 jar 包冲突。

首先确保 pom.xml 的修改已生效,再利用 Maven Helper 插件,寻找冲突的依赖,根据报错信息,把不想的包 exclude 掉,重新加载 pom.xml。

如果报错的包根本不在冲突列表里,也有可能是以下情况:

  • 版本不对, 则 google 一下相关报错,设置成正确的版本
  • 引入了多余的包,执行了不想要的逻辑

exclue掉:
image.png

重新加载:
image.png

自动import

image.png
image.png

文件乱码

如图所示,根据情况修改即可:
image.png

autowired 提示变量未赋值

这是因为我使用的是社区版open in new window,需要手动设置下open in new window

该方法可以放心使用。
虽然说的是 suppress unsed warning,其实是 suppress never assigned warning, unsed warning 还是会生效的。

上次编辑于:
贡献者: levy
- + diff --git a/java/Resolving-Common-Problems-in-Maven.md.html b/java/Resolving-Common-Problems-in-Maven.md.html index 760001cb..603202b1 100644 --- a/java/Resolving-Common-Problems-in-Maven.md.html +++ b/java/Resolving-Common-Problems-in-Maven.md.html @@ -5,7 +5,7 @@ - Maven常见问题与解决方案 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

Maven常见问题与解决方案

JavaDaily

Maven常见问题与解决方案

运行 class 找不到主类

maven compile
+    
跳至主要內容

Maven常见问题与解决方案

JavaDaily

Maven常见问题与解决方案

运行 class 找不到主类

maven compile
 

得到 class 文件后

cd /my-app/target/com/mycompany/app
 java App
 

报错:

错误: 找不到或无法加载主类 App
原因: java.lang.NoClassDefFoundError: com/mycompany/app/App (wrong name: App)

这是因为主类并非在默认包下,故需要在正确的路径下调用全限定名。

cd /my-app/target
@@ -78,7 +78,7 @@
 </repository>
 
 

或命令行强制不使用依赖:

mvn -U clean install
-

参考资料

官网:https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.htmlopen in new window
书籍:《Maven实战》

上次编辑于:
贡献者: levy
- +

参考资料

官网:https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.htmlopen in new window
书籍:《Maven实战》

上次编辑于:
贡献者: levy
+ diff --git a/java/avoid-sending-password-in-plaintext.html b/java/avoid-sending-password-in-plaintext.html index 71e9aeb5..70143d4b 100644 --- a/java/avoid-sending-password-in-plaintext.html +++ b/java/avoid-sending-password-in-plaintext.html @@ -5,7 +5,7 @@ - 避免密码明文传输 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

避免密码明文传输

JavaJavaScriptDaily

避免密码明文传输

说明

密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。

本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。

流程说明:前端加密,后端解密。

当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。

前端代码

记得安装相应的 npm 模块。

/*******************************
+    
跳至主要內容

避免密码明文传输

JavaJavaScriptDaily

避免密码明文传输

说明

密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。

本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。

流程说明:前端加密,后端解密。

当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。

前端代码

记得安装相应的 npm 模块。

/*******************************
 Description: aes加解密工具方法
 ********************************/
 import AES from 'crypto-js/aes'
@@ -118,7 +118,7 @@
     }
 }
 
-
上次编辑于:
贡献者: levy
- +
上次编辑于:
贡献者: levy
+ diff --git a/java/check-if-name-exists.html b/java/check-if-name-exists.html index b6d6331d..4f247191 100644 --- a/java/check-if-name-exists.html +++ b/java/check-if-name-exists.html @@ -5,7 +5,7 @@ - 检查名字是否重复 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

检查名字是否重复

JavaMySQLDaily

检查名字是否重复

检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。

推荐做法

借助数据库的来实现,执行以下语句:

ALTER TABLE my_table ADD UNIQUE(name);
+    
跳至主要內容

检查名字是否重复

JavaMySQLDaily

检查名字是否重复

检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。

推荐做法

借助数据库的来实现,执行以下语句:

ALTER TABLE my_table ADD UNIQUE(name);
 

然后,在程序里添加全局异常处理类:

@Slf4j
 @RestControllerAdvice
 public class GlobalExceptionHandler {
@@ -63,6 +63,6 @@
     if (exists(req)) throw ApplicationException("Duplication!")
   }
 

实践的经验表明:改得多,错的多!

上次编辑于:
贡献者: levy
- + diff --git a/java/common-practices-for-handling-excel.html b/java/common-practices-for-handling-excel.html index d747b39d..43c2b0ba 100644 --- a/java/common-practices-for-handling-excel.html +++ b/java/common-practices-for-handling-excel.html @@ -5,7 +5,7 @@ - Excel处理常用实践 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

Excel处理常用实践

JavaDaily

Excel处理常用实践

基础知识

导入需要用到对象,MultipartFile。

@PostMapping("/import")
+    
跳至主要內容

Excel处理常用实践

JavaDaily

Excel处理常用实践

基础知识

导入需要用到对象,MultipartFile。

@PostMapping("/import")
 public boolean importLicense(
       @RequestParam("file") MultipartFile file,
       @RequestParam("tenantId") @NotBlank String tenantId,
@@ -154,6 +154,6 @@
     parameters.put(parameterNames[i], parameterValues[i]);
 }
 
上次编辑于:
贡献者: levy
- + diff --git a/java/how-to-convert-snapshot-into-release-jar-without-source-code.html b/java/how-to-convert-snapshot-into-release-jar-without-source-code.html index 1e43cb8d..4a5729a5 100644 --- a/java/how-to-convert-snapshot-into-release-jar-without-source-code.html +++ b/java/how-to-convert-snapshot-into-release-jar-without-source-code.html @@ -5,7 +5,7 @@ - 奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包 | levy @@ -34,14 +34,14 @@ } - + -
跳至主要內容

奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包

Java

奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包

背景

项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。

问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。

下载


首先我们登录仓库,输入 ArtifactId,找到对应的 snapshot jar 包,并点击进入详情。


找到关键的三个 jar:

  • x.jar
  • x-sources.jar
  • x.pom

分别点击进入详情

依次点击 Path,下载到本地。

修改


首先修改名字,如图所示。

再使用任意工具解压 x.jar,提取出两个文件夹。进入 META-INF


修改 MANIFEST.MF,把里面的 snapshot 字符串去掉(如果有的话)


再点击 maven 文件夹


修改 pom.xml、pom.properties 文件,把里面的 snapshot 字符串去掉。

上传

把前面解压出来的文件重新打包成 jar

jar cvf my-1.4.1.jar com META-INF
+    
跳至主要內容

奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包

Java

奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包

背景

项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。

问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。

下载


首先我们登录仓库,输入 ArtifactId,找到对应的 snapshot jar 包,并点击进入详情。


找到关键的三个 jar:

  • x.jar
  • x-sources.jar
  • x.pom

分别点击进入详情

依次点击 Path,下载到本地。

修改


首先修改名字,如图所示。

再使用任意工具解压 x.jar,提取出两个文件夹。进入 META-INF


修改 MANIFEST.MF,把里面的 snapshot 字符串去掉(如果有的话)


再点击 maven 文件夹


修改 pom.xml、pom.properties 文件,把里面的 snapshot 字符串去掉。

上传

把前面解压出来的文件重新打包成 jar

jar cvf my-1.4.1.jar com META-INF
 
 # you can also use zip 
 # zip -r my-1.4.1.jar com META-INF
 

然后上传仓库

在Upload中,点击 maven-releases


把三个文件添加上去,点击 Upload。


可以看到,新的 release 版本的 jar 包已经在仓库中了,可以被安装使用了。

上次编辑于:
贡献者: levy
- + diff --git a/java/index.html b/java/index.html index 9b568a36..4a425d64 100644 --- a/java/index.html +++ b/java/index.html @@ -34,10 +34,10 @@ } - + - - +
跳至主要內容

Java


+ diff --git a/java/recommend-practices-for-collections-naming-convention.html b/java/recommend-practices-for-collections-naming-convention.html new file mode 100644 index 00000000..2b153265 --- /dev/null +++ b/java/recommend-practices-for-collections-naming-convention.html @@ -0,0 +1,88 @@ + + + + + + + + 集合命名推荐 | levy + + + + + + +
跳至主要內容

集合命名推荐

JavaDaily

集合命名推荐

概述

建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。

当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章?
image.png

这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。

List

List 的变量,一般以 List 或 s 结尾, 如 idList 或 ids。这点易于理解,大家也容易遵守。

坏的示例:

nodeType.forEach(t -> {
+    // 省略代码
+});
+

第一眼看到这代码的时候,不知道读者是什么反应?

按照习惯,nodeType 通常要么是字符串、数字、或枚举,但上述居然能调用 forEach 方法?我不禁愣了一下,赶紧去看了下定义,才发现原来是List。

好的示例:

nodeTypes.forEach(t -> {
+    
+});
+
+// 或
+nodeTypeList.forEach(t -> {
+    
+});
+

Set

参考List,在变量后面加 Set 即可。

Map

Map 的变量命名是值得重点关注,因为很容易造成差可读性的重灾区。

Map 的变量,推荐根据 key 与 value 来命名。规则表达式为:${key} + To + ${value} + Map,如 idToNameMap。
其中:

  • 可以玩一下“文字游戏”,把 To 写成 2(就像把 For 写成 4,这种 word play 是可以接受的),即 id2NameMap
  • 如果名字够清晰或已经很长,Map 可以省略,如 id2Name

为什么推荐这样命名?我们先来看一则案例,看一看经典的 Map<String, String> 在实际编码中,命名是如何造成理解上的困难的。

List<TableNode> tableNodes = getFromSomePlace();
+Map<String, String> tableIdMaps = getFromAnotherPlace(); 
+
+// 重点看下面两行代码
+Map<String, String> tableMaps = getTableIdMap(tableNodes, tableIdMaps); 
+replaceNodeId(tableNodes, tableMaps);
+

getTableIdMap核心实现如下:

// 谜之代码
+tableNodes.forEach(tableNode -> {
+    String tableNodeId = getTableNodeId(tableIdMaps);
+    tableMaps.put(tableNode.getNodeId(), tableNodeId);  //???
+});
+
+return tableMaps;
+

replaceNodeId核心实现如下:

// 谜之代码
+tableNodes.forEach(tableNode -> {
+    String nodeId = tableMaps.get(tableNode.getNodeId());  // ???
+    tableNode.setNodeId(nodeId);
+});
+

不知读者是否已经晕了?反正我是一头雾水。可能以为是因为我删减了很多代码导致的?恰恰相反,实际代码还有更多的逻辑判断,我已经抽出了核心部分,不需要被其他逻辑干扰了。

上面的代码带来的疑问有:
疑问1:都是 Map<String, String>tableIdMapstableMaps 有什么区别,它们存储的到底是什么?从类型上看,也不像是 id -> table 的映射啊。
疑问2:tableMaps.put(tableNode.getNodeId(), tableNodeId); 这个 tableNode.getNodeId() 不是等于 tableNodeId吗?
疑问3:String nodeId = tableMaps.get(tableNode.getNodeId()); 根据 nodeId 拿到 nodeId?

到这里已可以猜到,tableMaps里的 key 与 value 肯定不是单纯的 nodeId 的意思,但这并没有什么帮助,因为我们还是不知道 tableMaps key -> value 映射的到底是什么。

我们来看修改变量名之后,上述代码的效果。

List<TableNode> tableNodes = getFromSomePlace();
+//修改下面两个 Map 的命名
+Map<String, String> str2TableIdMap = getFromAnotherPlace(); 
+Map<String, String> nodeId2TableIdMap = getTableIdMap(tableNodes, str2TableIdMap); 
+
+replaceNodeId(tableNodes, nodeId2TableIdMap);
+
tableNodes.forEach(tableNode -> {
+    String tableId = getTableNodeId(str2TableIdMap); // 修改了这行
+    nodeId2TableIdMap.put(tableNode.getNodeId(), tableId); // 修改了这行
+});
+
+return nodeId2TableIdMap;
+
tableNodes.forEach(tableNode -> {
+    String tableId = nodeId2TableIdMap.get(tableNode.getNodeId());  //修改了这行
+    tableNode.setNodeId(tableId);
+});
+

现在是不是好懂很多了:

  • str2TableIdMap 存储的 str -> tableId 的映射, 其中 str 是由某种规则拼接而成的字符串,具体规则封装在了 getTableNodeId这个函数里,我们暂时可以不用关心
  • nodeId2TableIdMap 存储的是 nodeId -> tableId 的映射

仅仅修改变量名,可读性就有大大提高,效果立竿见影!

上次编辑于:
贡献者: levy
+ + + diff --git a/java/recommend-practices-for-query-by-date-range.html b/java/recommend-practices-for-query-by-date-range.html index 653f408f..60502feb 100644 --- a/java/recommend-practices-for-query-by-date-range.html +++ b/java/recommend-practices-for-query-by-date-range.html @@ -5,7 +5,7 @@ - 根据时间范围查询推荐实践 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

根据时间范围查询推荐实践

JavaDaily

根据时间范围查询推荐实践

背景

不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。

虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。

本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson

需求

删除某个时间段以前的日志。类似于消除浏览记录:

分析

上图只是UI示例,为了适应UI的变化,最好把接口设置成接收两个参数:

  • 开始时间
  • 结束时间

如果UI如上图所示,则选择过去 7 天时,开始时间就是 1970 年 1 月 1 日,结束时间就是过去第 7 天。

实现

MySQL

对于 MySQL,推荐使用,因为简单直观,且方便:

SELECT * FROM operation_logs 
+    
跳至主要內容

根据时间范围查询推荐实践

JavaDaily

根据时间范围查询推荐实践

背景

不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。

虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。

本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson

需求

删除某个时间段以前的日志。类似于消除浏览记录:

分析

上图只是UI示例,为了适应UI的变化,最好把接口设置成接收两个参数:

  • 开始时间
  • 结束时间

如果UI如上图所示,则选择过去 7 天时,开始时间就是 1970 年 1 月 1 日,结束时间就是过去第 7 天。

实现

MySQL

对于 MySQL,推荐使用,因为简单直观,且方便:

SELECT * FROM operation_logs 
 WHERE created_time BETWEEN '2023-09-12 11:44:26' AND '2023-09-12 13:54:52';
 

另一种方式:

SELECT * FROM operation_logs 
 WHERE created_time >= '2023-09-12 11:44:26' AND created_time <= '2023-09-12 13:54:52';
@@ -96,6 +96,6 @@
 }
 
 

结语

好了,终于搞完了,我的评价是:

上次编辑于:
贡献者: levy
- + diff --git a/java/recommend-practices-for-writing-good-functions.html b/java/recommend-practices-for-writing-good-functions.html new file mode 100644 index 00000000..c0e02a2c --- /dev/null +++ b/java/recommend-practices-for-writing-good-functions.html @@ -0,0 +1,104 @@ + + + + + + + + 编写函数的最佳实践 | levy + + + + + + +
跳至主要內容

编写函数的最佳实践

JavaDaily

编写函数的最佳实践

前言

编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。

本文将推荐一些编写函数的最佳实践,以供参数。

减少重复

这是在遵守 Don't repeat yourselfopen in new window (DRY) 原则。

实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。

然而,有时代码只是相似,不完全相同,不能简单地使用 IDEA 右键 + Refactor + Extract Method 来抽取函数。
此时,为减少重复,需要进行一些思考。

可以把程序的划分成三个部分:

Program = Control + Logic + Data Structure
+

一般而言,函数的入参都是数据变量,也即 Data Structure。
而 Java 8 以后,lambda 表达式(也即函数)可以作为入参,其代表的是 Logic。
因此,最抽象的函数,是只定义了 Control、把 Logic 及 Data Structure 都作为入参的函数。当遇到类似却不完全相同的代码、想封装函数有遇难时,可以借助上述思路来梳理逻辑。

隐藏细节

隐藏细节,是为了减少使用者的心智负担,方便其调用。

有一个简单的判断标准:如果调用者需要频繁查看函数内部情况,以确定函数的目的或实现细节,那么隐藏细节的意图是失败的。

建议

为了达到前文所述的目的,如以下实践建议。需要指出的是,以下提倡的是建议,并非金科玉律;只适用于一般情况,并非所有情况,特殊情况是可以特殊处理的。

优先根据业务命名

一般而言,函数名最好是根据业务逻辑、结合业务领域来命名,而不是根据程序逻辑来命名。

示例:

// bad
+String getString(UserDTO user);
+
+// good
+String getUsername(UserDTO user);
+

当然,如果有些方法名是专业名词或是耳熟能详的,那直接使用即可,如:

void bfs();
+
+void shortestPath();
+
+void binarySearch();
+

一个函数只做一件事

遵守 Keep it simple stupidopen in new window (KISS) 原则。

当然,不可能所有函数都达到这个要求——程序入口一般就会做很多事。我们的目标是尽可能地遵守该原则,减少调用者需要频繁查看函数实现的可能。

反例1:做A且做B

// bad
+int doSomethingAndAnother(Param param);
+
+// good 
+// 拆分成两个函数
+int doSomething(ParamA a);
+int doAnother(ParamB b);
+

坏的示例问题出在哪里呢?根据入参的合法性,有可能产生以下情况:

  1. 参数合法,同时做A与B;只要有一个参数不合法,均不做A与B
  2. 哪个合法就做哪个,也即可能出现:
    1. 只做A
    2. 只做B
    3. 做A也做B
    4. 二者均不做

到底是什么情况呢?对此疑问,调用方只有查看函数实现,才能了解,于是破坏了封装的意图。
而且,坏的示例还会存在一个问题:如果调用方只想做A怎么办?我想很少人会把原代码拆分成两个函数,更常见的做法是保持原函数不变,并拷贝原函数的部分逻辑,封装一个只做A的新函数——这就造成了代码的冗余,于是减少重复的目的失败了。

反例2:做A或做B

// bad
+int doSomethingOrAnother(Param param);
+
+// good 
+// 拆分成两个函数
+int doSomething(ParamA a);
+int doAnother(ParamB b);
+

同样的,坏的示例会让人疑惑,搞不清楚函数的意图到底属于以下哪种情况:

  1. 要么做A,要么做B,一定会做其中一个
  2. 哪个合法做哪个,可能会出现:
    1. 只做A
    2. 只做B
    3. 做A也做B
    4. 二者均不做

当然,一些常见的深入人心的 API,我们是可以接受这种“或逻辑”的:

  • saveOrUpdate() // 有 id 就是 update,没有就是 insert
  • getOrDefault() // 获取值;如果值不存在,就返回默认值

优先使用纯函数

纯函数(pure function),可以借助数学中的函数概念来理解:y = f(x)

  • 给定 x,能返回确定的 y
  • 无论函数调用几次、在何处调用,上述结果都不会变化

纯函数的好处之一是无副作用(side effect)。也即调用函数后,不会对函数作用域以外的变量造成影响。

反例:一个常见的现象,辅助函数会修改入参,主函数的变量生命周期贯穿整个主函数

Result getRelation(String nodeId, Param param) {
+    // 省略其他代码
+    
+    Map<Long, Node> nodesMap = new HashMap<>();   
+
+    // 没有返回值
+    getUpstream(nodeId, nodesMap, param);
+
+    // “废物利用”!
+    nodesMap.clear();
+
+    // 没有返回值
+    getDownstream(nodeId, nodesMap, param);
+  
+    Map<Long, NodeDTO> nodesDtoMap = new HashMap<>();
+    return getResult(nodesDtoMap, nodesMap);
+} 
+

因为已经省略了其他代码,因此我们不难看出 nodesMap是辅助变量,是为返回结果而服务的,而没有返回值的函数调用很可能修改了该变量。

但实际上,代码逻辑很长,还有其他变量掺杂其中,代码意图并非能够一目了然。假设稍微修改一下,为 getUpstream()添加多一个参数,还能看出函数到底修改了哪个变量吗?

Map<Long, Node> nodesMap = new HashMap<>(); 
+// 添加多一个变量
+Map<Long, Edge> edgesMap = new HashMap<>();
+
+getUpstream(nodeId, nodesMap, edgesMap, param);
+

如果 edgesMap是来自主函数的参数呢?

Result getRelation(String nodeId, Map<Long, Edge> edgesMap, Param param) {
+    // 省略其他代码
+    
+    Map<Long, Node> nodesMap = new HashMap<>();   
+
+    // edgesMap 是主函数传参
+    getUpstream(nodeId, nodesMap, edgesMap, param);
+       
+    // 省略其他代码 
+} 
+

情况变得糟糕了,因为按照 getUpstream()会修改入参的“习性”,我们很难有信心认为 edgesMap一定没有被修改。

上述例子是想表明:为了贪图方便,编写一个不需要返回值而直接修改入参的函数,会给后续的维护增加负担。一方面变量状态难以追踪,另一方面这样的函数也不方便测试。

一般而言,优先使用纯函数,会助于对大函数的拆分,从而使得 KISS 原则更容易被遵守。

当然,总有例外情况。当程序逻辑复杂时,或有些函数就是对 setter 语句的调用,此时不需要返回值并且会造成副作用,又该怎么办呢?请看下一条建议。

编写不需要返回值的函数

有如下建议:

  1. 方法名叫setup+ $
  2. 一个方法只修改一个变量
  3. 要修改的变量就是函数的第一个参数
  4. lambda(如果有的话) 作为最后的参数

示例:

setupNodeCommonInfo(node, nodeId, queryUpstream);
+setupNodeTableInfo(node, nodeId2Table);
+
+setupEdgeCrossLayer(edges, Edge::getSourceId, Edge::getTargetId);
+

参考资料

上次编辑于:
贡献者: levy
+ + + diff --git a/java/using-enum-in-java.html b/java/using-enum-in-java.html new file mode 100644 index 00000000..327b716f --- /dev/null +++ b/java/using-enum-in-java.html @@ -0,0 +1,116 @@ + + + + + + + + 枚举的推荐实践 | levy + + + + + + +
跳至主要內容

枚举的推荐实践

JavaDaily

枚举的推荐实践

背景

定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。

而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。

本文推荐,不要在枚举中定义数字,直接使用枚举名即可!

Java

极简实现

理想状态下,枚举就应该这样简单!

public enum SEX {
+  MALE,
+  FEMALE;
+}
+

常见实现

然而,代码库中常见的实现是,使用 enum 关键字定义枚举类型,并写下枚举名以及相应的绑定值

public enum SEX {
+  MALE(1, "this is description for male"),
+  FEMALE(0, "this is description for female"); 
+}
+
+

就这点代码,从业务的角度讲,逻辑已经实现了。

但从具体编程语言(Java)的角度讲,工作还没有完成:此时,编译器会提示报错,因为缺少构造函数。

站在调用方的角度,就会发现,我们没有方法拿到枚举里的定义值,也即(1, "this is description for male")

因此,还需要编写以下内容:

  • final 的成员变量
  • 在构造函数中为成员变量赋值
  • 定义成员变量相应的 getter 方法
  private final Integer code;
+  private final String desc;
+
+  MyEnum(int code, String desc) {
+    this.code = code;
+    this.desc = desc;
+  }
+
+  public Integer getCode() {
+    return code;
+  }
+
+  public String getDesc() {
+    return desc;
+  }
+

前面说到,我们期望枚举里有 key-value 的功能,则还要再定义 getValueByKey 方法:

  public static String getDescByCode(Integer code) {
+    for (MyEnum e : values()) {
+      if (e.getCode().equals(code)) return e.getDesc();
+    }
+    return "";
+  }
+

有了上面的方法,我们才可以通过传入 code,返回相应的 desc

如果反过来,我们想通过传入 desc 返回相应的 code 呢?还得再写一个方法!真是烦琐!

更好的方式

前面我们可以看到,当关键逻辑写出来以后,还要写那么多模板代码,简直索然无味。

为什么把简单的事情搞复杂,有没有更好的方式?

需要了解到以下事实,枚举类型提供了通过枚举名获得枚举值的方法:

  public static void main(String[] args) {
+    MyEnum myEnum = MyEnum.valueOf("MALE");
+    System.out.println(myEnum.getDesc());
+  }
+

也就是说,如果使用 valueOf的方式来定位枚举值,就可以通过 getter 方法来获取自定义的业务值,不需要再写自定义的 getValueByKey 方法。

为什么还要定义数字?

另外,再看之前的枚举定义,为什么要定义 MALE(1, "this is description for male")呢?
MALE本身已经有含义的情况下,为何还要再设置一个数字呢?

这个数字导致了不必要的转换:

  1. 前端传数字 -> 后端转成枚举
  2. 后端再把枚举转成数字 -> 存入数据库

最蛋疼的就是,select 数据库数据的时候,全是1、2、3,都不知道什么意思。

能不能不要这个数字? 直接把定义好的枚举名,存入数据库呢?

表面上看,这是因为数据库定义如此——从数据库查出来是数字,因而要根据数字去获取别的业务信息。

然而,我再问了一下,得到的回答是: 上述做法是设计如此。为了节省存储空间及提升查询性能,才在数据库设置的数字,而不是直接存储字符串。

这就引出了另一个问题,需要这样做来提高性能吗?对此,我们接下来要进行数据库层面的讨论。

数据库

数据库本身是支持检举类型的,下文以 MySQL 为例进行说明。

枚举相关的 SQL 语句示例:

-- 建表
+CREATE TABLE shirts (
+  name VARCHAR(40),
+  size ENUM('x-small', 'small', 'medium', 'large', 'x-large')
+);
+
+-- 插入
+INSERT INTO shirts (name, size) VALUES ('dress shirt','large'), ('t-shirt','medium'),
+('polo shirt','small');
+
+-- 查询
+SELECT name, size FROM shirts WHERE size = 'medium';
++---------+--------+
+| name    | size   |
++---------+--------+
+| t-shirt | medium |
++---------+--------+
+

存储方式:转换成数字存储,查询时再转换成字符串
存储空间open in new window:1 or 2 bytes (65,535 values maximum)

由上可知,为了节省空间及提高查询性能,在数据库层面使用数字代表枚举进行存储,是不必要的,因为数据库本身已有相应的功能。

排序特点

排序规则如下(如执行order by 时):

  • NULL 在最前面,'' 次之,接下来是非空的枚举值
  • 定义枚举值时的顺序,就是排序的顺序

推荐使用以下技巧:

  • 按字母表顺序定义检举值
  • 把检举值转换成字符串再排序 ORDER BY CAST(col AS CHAR) or ORDER BY CONCAT(col)

添加新值

使用枚举类型的最大的问题是,后续添加新值时需要执行 alter table。

如果枚举值经常变动且对枚举值的顺序要求(添加的新值不一定在最后面),则不建议使用枚举类型。

否则的话,可以使用枚举类型。

因为 alter table 的机制是:

  1. 创建临时表 t'
  2. 插入数据
  3. 删除当前表 t
  4. 把 t' 重命名为 t

这在表数据量较大时,会导致表被锁较长时间而不可用。

可以使用以下技巧进行更新,记得严格按照顺序执行:

CREATE TABLE database.shirts_tmp LIKE database.shirts;
+-- 在最后添加了 'xx-large'
+ALTER TABLE database.shirts_tmp MODIFY COLUMN ENUM('x-small', 'small', 'medium', 'large', 'x-large', 'xx-large');
+
+FLUSH TABLES WITH READ LOCK;
+
+SHOW variables LIKE 'datadir';
+SELECT DATABASE() as databaseName;
+
# 登录 MySQL 所在机器
+# 根据上面最后两行SQL,进入相关目录
+cd ${datadir}/${databaseName}
+
+mv shirts shirts_old;
+mv shirts_tmp shirts;
+mv shirts_old shirts_tmp;
+
UNLOCK TABLES;
+
+-- 可以在删除前检查两张表的定义是否已交换
+-- select * from database.shirts;
+-- select * from database.shirts_tmp;
+
+DROP TABLE database.shirts_tmp;
+

如果在 Navicat 或 DBeaver等图形工具上看不出表结构的变化,请刷新数据库。

总结

本文主要目的是想消除枚举中对魔法数字的误用,试图让 Java 代码、数据库数据、以及前端 API 传参都使用可理解、可读性强的枚举值。

在数据库层面,对于枚举类型字段的注意点,本文也做了说明。如果实在不想每次添加新的枚举值都执行 alter table语句,贪图省事,使用 varchar 存储也未尝不可。

总之,本文想强调的是 Java 代码与数据库数据展示内容的一致性,至于数据库的存储格式,是见仁见智的。

参考资料

上次编辑于:
贡献者: levy
+ + + diff --git a/java/which-one-is-better-Boolean-or-boolean.html b/java/which-one-is-better-Boolean-or-boolean.html new file mode 100644 index 00000000..3cfa4291 --- /dev/null +++ b/java/which-one-is-better-Boolean-or-boolean.html @@ -0,0 +1,58 @@ + + + + + + + + Boolean 还是 boolean? | levy + + + + + + +
跳至主要內容

Boolean 还是 boolean?


Boolean 还是 boolean?

在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢?

结论

先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。

原文如下:
image.png

红线处翻译:总结就是,当你有得选的时候,请务必使用基本类型,而非包装类型。

那什么时候使用包装类型呢?原文如下:
image.png

红线处翻译:当你没得选、被强制要求时,才使用包装类型。如:使用泛型(使用集合类、调用参数是泛型参数的方法),以及通过反射进行方法调用(使用 invoke 方法)

争议

阿里的《Java开发手册》open in new window有提到,类属性强制使用包装类型。
image.png文中举的例子还是有道理的:有时需要 null 来表示额外的信息。

但注意,本文讨论的仅仅是布尔类型,不要发散话题。 那现在就来分析一下,布尔类型有没有必要考虑 null 的情况?

我认为是没有的必要的。理由如下:

  • 布尔类型就是二进制的,代码两种情况:1或0;真或假。使用包装类型,出现第三种情况 null,不但要注意空指针异常问题,还要兼容 null 的情况——此时到底是真还是假呢?
  • 如果 null 表示的既不是真也不假,而是第三种情况——就不该定义为布尔类型,而应定义为枚举类型,因为一共有三种情况。使用 Boolean 来表示三种情况,是设计上的偷懒。

实战

我们来看一下实际代码中,滥用 Boolean 类型导致的问题。

简单例子

有如下 Controller,使用的是 boolean:

@RestController
+public class DemoController {
+  @RequestMapping("/hello")
+  public String hello(boolean bool) {
+   return String.valueOf(bool) ;
+  }
+}
+

前端传个空值,会得到报错信息:
image.png
但如果使用 Boolean,并不能检查出是非法参数:
image.png
这原本是前端传参错误,但却无法即使发现。

你可能会问,为什么不先判断 Boolean 变量是否为 null 呢?因为别人在声明 Boolean 变量时,设置了默认值为 false,谁能想到前端会传个 null?

这就是 Boolean 的问题:“千防万防,家贼难防”!

复杂例子

下面是前端传给 Controller 的参数:

public class TableQuery {
+    private Boolean withTable2Api;
+    private Boolean forkable;
+}	
+

需要注意的是,有至少两个 Controller 会接受到了这个参数,并且根据业务的不同,它们对参数的处理是不同的:

  • 有的会判断如果 withTable2Api 为 null,就设置为 true
  • 有的则完全由前端传值,也即 forkable、withTable2Api 有可能为 null

来看一下,Service 里的相应代码:
image.png
这里有什么问题呢?可以看到,第二个 if 使用 !Boolean._TRUE_去判断很别扭,然而,却不能改写成:

// 错误!因为没有兼容 null 的情况
+if (Boolean.FALSE.equals(query.getWithTable2Api())) {
+   
+}
+

这就是布尔值使用 Boolean 类型在实战中最大的问题——你不能任意地进行真或假的判断,而必须兼容上下文中隐式对 null 赋予的含义。

而多人协作过程中,外部调用是很难控制的,因此,此时使用 Boolean,只增加了无谓的编码负担。

Joshua Bloch - Effective Java (3rd) - 2018.pdfopen in new window

上次编辑于:
贡献者: levy
+ + + diff --git a/java/which-one-is-better-forEach-or-map.html b/java/which-one-is-better-forEach-or-map.html new file mode 100644 index 00000000..f3d3dac5 --- /dev/null +++ b/java/which-one-is-better-forEach-or-map.html @@ -0,0 +1,113 @@ + + + + + + + + forEach 还是 map? | levy + + + + + + +
跳至主要內容

forEach 还是 map?


forEach 还是 map?

背景

遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有:

List<Type> result = new ArrayList<>();
+list.forEach(src -> {
+    Type target = BeanUtils.copyProperties(src, target);
+    //省略代码
+    result.add(target);
+});
+
List<Type> result = list.map(src -> {
+    Type target = BeanUtils.copyProperties(src, target);
+    //省略代码
+   return target;
+});
+

两种方式看起来没多大差别啊,到底用哪种呢?

结论

先说结论:根据《Effective Java》(第三版),forEach 只用于消费数据的场景,并不应该用于计算、累加,故上述代码应该使用 map。

原文如下:
image.png

红字处翻译:forEach 仅适用于输出 stream 里的计算结果,并不适合执行计算。

解析

为更好地理解上述结论,需要先理解以下内涵:
image.png
Stream 的引入,不仅带来新的语法,也带来了函数式编程的思维。

这里最重要的一点就是:编写纯函数(pure function),不造成副作用(side-effect)。

纯函数可以用数学中的函数映射来理解:y = f(x)

  • 给定 x,能唯一确定 y
  • 无论函数调用几次、在何处调用,上述结果都不会变化

无副作用意思是:调用函数后,不会对函数作用域以外的变量造成影响。而纯函数,一定是无副作用的。

前文使用 forEach 的代码,其实是造成了副作用的:

List<Type> result = new ArrayList<>();
+list.forEach(src -> {
+    Type target = BeanUtils.copyProperties(src, target);
+    result.add(target); //side effect
+});
+

很简单的一个识别方法:再调用一次 forEach,result 的结果还是期望的结果吗?显然不是。

但如果 map 方法呢?再调用一次,结果不变!

List<Type> result = list.map(src -> {
+    Type target = BeanUtils.copyProperties(src, target);
+   return target; // side effect free
+});
+

类似的,修改函数入参,也不是纯函数:

List<String> ids = new ArrayList<>();
+
+collectIds(data, ids); // 在这里填充 ids!
+
+int size = ids.size(); 
+

上面的 collectIds函数是令人讨厌的——写代码的人懒得写函数返回时,直接修改函数入参,给后面维护的人留下隐患。

当然,如果一定要用纯函数来看待问题,未免过于理想化,因为有时要执行这样的代码:

list.forEach(v -> {
+    myService.save(v);
+});
+

虽然上述代码并没影响到函数作用域以外的代码变量,但 myService 会把数据持久化,站在整个应用的角度讲,仍然造成了副作用。

但上述代码可以接受的。因此,建议记住 forEach 只用于消费数据,不用于计算及返回,就不容易混淆。

实战

当然,上述的讨论还是比较偏理论的,我们来看一下实际项目中,滥用 forEach 可能导致可读性较差的问题。

代码一开始,是简单清晰的:

// 一个只有20行的函数
+void myFunction(List<Node> nodes, List<Edge> edges) {
+    Type1 var1;
+    Type2 var2;
+    
+    nodes.forEach(node -> {
+        // 修改 var1
+    })
+      
+    edges.forEach(edge -> {
+        // 修改 var2
+    })
+}
+

然而,业务会变化,逻辑会复杂,代码也要修改。而上述在 forEach 中修改变量的行为,罪恶的根源在于,它在向后来修改代码的人发出邀请:新增的逻辑,写在这个 forEach 里面就好了!

当仅在 forEach 添加代码就能完成任务的时候,很难有人能抵抗这种诱惑,于是就会变成:

// 一个超过50行的函数
+void myFunction(List<Node> nodes, List<Edge> edges) {
+    Type1 var1;
+    Type2 var2;
+    Type3 var3;
+    Type4 var4;
+    Type5 var5;
+    
+    nodes.forEach(node -> {
+        // 里面有多个 if-else
+        // 修改了 var1, var2
+        // 有可能修改 var5
+    })
+      
+    edges.forEach(edge -> {
+        // 里面有多个 if-else
+        // 修改了 var3, var4
+        // 有可能修改 var5
+    })
+}
+

写代码一时爽,维护火葬场。上面的代码,将是维护的噩梦!

并且,如果维护者只想知道函数的整体逻辑,由于变量穿插、隐藏在 forEach 内部,维护者不得不在各种 if-else 里面追踪变量,很容易就陷入不必要的细节中。

如果换个方式来写呢?

void myFunction(List<Node> nodes, List<Edge> edges) {   
+    // 为代码简洁,省略了 collect(Collectors.toList())
+    Type1 var1 = nodes.map(v -> getVar1(v));
+    Type2 var2 = nodes.map(v -> getVar2(v));
+    Type3 var3 = edges.map(v -> getVar3(v));
+    Type4 var4 = edges.map(v -> getVar4(v));
+    Type5 var5 = Stream.of(nodes, edges).filter(v -> filterVar5(v));
+    
+}
+

效果有大大的不同!

现在想追查哪个变量,简单轻松好多啦!

上次编辑于:
贡献者: levy
+ + + diff --git a/java/why-i-prefer-fastjson-instead-of-jackson.html b/java/why-i-prefer-fastjson-instead-of-jackson.html index 22b61467..36db46f9 100644 --- a/java/why-i-prefer-fastjson-instead-of-jackson.html +++ b/java/why-i-prefer-fastjson-instead-of-jackson.html @@ -5,7 +5,7 @@ - Jackson 经典异常 UnrecognizedPropertyException | levy @@ -34,14 +34,14 @@ } - + -
跳至主要內容

Jackson 经典异常 UnrecognizedPropertyException

JavaDailyVideo

Jackson 经典异常 UnrecognizedPropertyException

原因是 json 包含的字段,多于 Java 实体类定义的字段。

解决方法很简单:

new ObjectMapper()
+    
跳至主要內容

Jackson 经典异常 UnrecognizedPropertyException

JavaDailyVideo

Jackson 经典异常 UnrecognizedPropertyException

原因是 json 包含的字段,多于 Java 实体类定义的字段。

解决方法很简单:

new ObjectMapper()
   .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
 

或者为相关实体添加注解:

@JsonIgnoreProperties(ignoreUnknown = true)
 public class ObjectParseFromJsonString {  }
 

可是,如果用 fastjson,根本不会有这种错误。使用起来也简单,文档在这里open in new window

所以,为什么不用 fastjson 呢?

上次编辑于:
贡献者: levy
- + diff --git a/java/why-is-it-so-hard-to-upgrade-dependencies.html b/java/why-is-it-so-hard-to-upgrade-dependencies.html index 5e024761..67a9e954 100644 --- a/java/why-is-it-so-hard-to-upgrade-dependencies.html +++ b/java/why-is-it-so-hard-to-upgrade-dependencies.html @@ -5,7 +5,7 @@ - 升个jar版本,怎么这么难? | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

升个jar版本,怎么这么难?

JavaDailyVideo

升个jar版本,怎么这么难?

前言

无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。

通常来说,对 Web 应用而言,前端是消费者,后端是提供者。

今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?

接口调用示意图

已知信息

  1. common.jar 的版本是 1.0.0-SNAPSHOT,实际上 common.jar 的最新稳定版已经是 5.x 了。比较了下版本内容,发现二者是不兼容的。
  2. 公司有多个业务系统依赖 infra-service,但它们隶属于别的团队

问题

infra-service 的某个接口,现在要修改,多返回一个字段,但因为之前的代码健壮性不够,新接口 common.jar 处理会报错。

于是,想通过让使用方升级 jar 来解决,此方案可行吗?

答案可能出乎意料:不行!

因为:jar 升级版本的动作不在自己的掌控范围内。

说起来很简单:叫他们升一下就行。
但问题是:

  1. 他们是谁?
  2. 你说升就升?

问题本身在技术层面很简单,但在现实的执行层面,有着超出技术范畴的难点。这便是服务提供者,需要面对的。而这是作为服务的消费者,容易忽视的一点。

复盘

那么,在技术层面,是否能做得更好,避免重蹈覆辙呢?其实是可以的。

有两种思路:

  1. 只对外发布稳定版,要么强制自己的接口一直向后兼容;要么一旦有接口不兼容,在保留旧接口的情况下,发布新接口、新 jar。
  2. 利用好 SNAPSHOT 版本 jar 会不断覆盖的特性,同时对外发布 1.0.0-SNAPSHOT 版本,以及每次迭代的稳定版。也即,通过使得 1.0.0-SNAPSHOT 永远与最新的稳定版本相同,让使用者及时升级。
上次编辑于:
贡献者: levy
- +
跳至主要內容

升个jar版本,怎么这么难?

JavaDailyVideo

升个jar版本,怎么这么难?

前言

无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。

通常来说,对 Web 应用而言,前端是消费者,后端是提供者。

今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?

接口调用示意图

已知信息

  1. common.jar 的版本是 1.0.0-SNAPSHOT,实际上 common.jar 的最新稳定版已经是 5.x 了。比较了下版本内容,发现二者是不兼容的。
  2. 公司有多个业务系统依赖 infra-service,但它们隶属于别的团队

问题

infra-service 的某个接口,现在要修改,多返回一个字段,但因为之前的代码健壮性不够,新接口 common.jar 处理会报错。

于是,想通过让使用方升级 jar 来解决,此方案可行吗?

答案可能出乎意料:不行!

因为:jar 升级版本的动作不在自己的掌控范围内。

说起来很简单:叫他们升一下就行。
但问题是:

  1. 他们是谁?
  2. 你说升就升?

问题本身在技术层面很简单,但在现实的执行层面,有着超出技术范畴的难点。这便是服务提供者,需要面对的。而这是作为服务的消费者,容易忽视的一点。

复盘

那么,在技术层面,是否能做得更好,避免重蹈覆辙呢?其实是可以的。

有两种思路:

  1. 只对外发布稳定版,要么强制自己的接口一直向后兼容;要么一旦有接口不兼容,在保留旧接口的情况下,发布新接口、新 jar。
  2. 利用好 SNAPSHOT 版本 jar 会不断覆盖的特性,同时对外发布 1.0.0-SNAPSHOT 版本,以及每次迭代的稳定版。也即,通过使得 1.0.0-SNAPSHOT 永远与最新的稳定版本相同,让使用者及时升级。
上次编辑于:
贡献者: levy
+ diff --git a/mysql/index.html b/mysql/index.html index ea29259b..48b478b8 100644 --- a/mysql/index.html +++ b/mysql/index.html @@ -34,10 +34,10 @@ } - + - - + + diff --git a/mysql/mysql-backup-case-study-mysqldump-in-action.html b/mysql/mysql-backup-case-study-mysqldump-in-action.html index 0a089e99..cfbedfb6 100644 --- a/mysql/mysql-backup-case-study-mysqldump-in-action.html +++ b/mysql/mysql-backup-case-study-mysqldump-in-action.html @@ -5,7 +5,7 @@ - 数据备份案例:mysqldump实战 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容

数据备份案例:mysqldump实战

DailyMySQL

数据备份案例:mysqldump实战

背景

前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。

并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。

在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。

架构


注意到:

  1. 从 mysql-b -> mysql-b',就要用到工具 mysqldump
  2. 关于 sql 的编写在另一文中已有提及,就不重复了
  3. 只能通过跳板机在终端连接数据库实例,因此无法使用图例界面操作。

安装

首先要在跳板机安装 mysqldump。

如果够幸运,可以用包管理工具安装:

sudo apt update
+    
跳至主要內容

数据备份案例:mysqldump实战

DailyMySQL

数据备份案例:mysqldump实战

背景

前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。

并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。

在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。

架构


注意到:

  1. 从 mysql-b -> mysql-b',就要用到工具 mysqldump
  2. 关于 sql 的编写在另一文中已有提及,就不重复了
  3. 只能通过跳板机在终端连接数据库实例,因此无法使用图例界面操作。

安装

首先要在跳板机安装 mysqldump。

如果够幸运,可以用包管理工具安装:

sudo apt update
 
 sudo apt install mysql-client
 
sudo yum install https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
@@ -62,6 +62,6 @@
 

示例命令:

mysqldump -u your_username -p your_database --ignore-table=table_name1 --ignore-table=table_name2 > dump.sql
 

注意:该参数一次只能忽略一张表,故忽略多张表需要声明多次。

导入

导入命令类似导出,只不过此时使用的是 mysql 客户端,并且重定向符号不同。

mysql -u your_username -p your_database_name < dump.sql
 

为什么不?

现在来回答为什么不直接复制MySQL的磁盘文件。

根据前面我们知道,也许我们的需求是部分备份,而不是全量备份,则直接拷贝磁盘文件,在数据量大的情况下,只会造成传输负担,反而“欲速则不达”。

另外,复制磁盘文件,为保证数据一致性,要求MySQL必须停止运行。主要有以下原因:

  1. Ative Transactions: Copying database files directly may lead to data inconsistency if there are active transactions or changes happening in the database while the files are being copied.
  2. Flush and Sync: The MySQL server may buffer data in memory and write it to disk periodically. When you copy files directly, you may capture data that is in memory and not yet written to disk.
  3. File Locks: Some storage engines, such as MyISAM, may use file-level locks, which can prevent you from copying files while the server is running. InnoDB, on the other hand, uses a different mechanism (tablespace files), but copying InnoDB files still carries the risk of data inconsistency.

因此,纵然有直接复制MySQL磁盘文件的奇技淫巧,还是不建议使用,我就也不向大家展示了。

上次编辑于:
贡献者: levy
- + diff --git a/mysql/mysql-data-migration-case-study-add-auto-increment.html b/mysql/mysql-data-migration-case-study-add-auto-increment.html index 90dbd071..11c141b8 100644 --- a/mysql/mysql-data-migration-case-study-add-auto-increment.html +++ b/mysql/mysql-data-migration-case-study-add-auto-increment.html @@ -5,10 +5,10 @@ - 数据迁移案例:表AUTO_INCREMENT加10w | levy +gtag('config','G-6HEW6B1S6B');数据迁移案例:表AUTO_INCREMENT加10w | levy