diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b813107d..ad961df7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,23 +66,23 @@ devDependencies: '@sveltejs/adapter-auto': 1.0.0-next.64 '@sveltejs/adapter-node': 1.0.0-next.86 '@sveltejs/adapter-static': 1.0.0-next.39 - '@sveltejs/kit': 1.0.0-next.405_svelte@3.49.0+vite@3.0.7 + '@sveltejs/kit': 1.0.0-next.405_svelte@3.49.0+vite@3.0.8 '@tailwindcss/typography': 0.5.4_tailwindcss@3.1.8 - '@types/node': 18.7.3 + '@types/node': 18.7.6 '@types/unist': 2.0.6 - '@typescript-eslint/eslint-plugin': 5.33.0_njno5y7ry2l2lcmiu4tywxkwnq - '@typescript-eslint/parser': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq + '@typescript-eslint/eslint-plugin': 5.33.1_vsoshirnpb7xw6mr7xomgfas2i + '@typescript-eslint/parser': 5.33.1_4rv7y5c6xz3vfxwhbrcxxi73bq autoprefixer: 10.4.8_postcss@8.4.16 chalk: 5.0.1 chokidar: 3.5.3 cross-env: 7.0.3 cssnano: 5.1.13_postcss@8.4.16 daisyui: 2.24.0_25hquoklqeoqwmt7fwvvcyxm5e - eslint: 8.21.0 - eslint-config-prettier: 8.5.0_eslint@8.21.0 - eslint-plugin-svelte3: 4.0.0_a7wk4ghvg4hia4trwaglu7p6cq + eslint: 8.22.0 + eslint-config-prettier: 8.5.0_eslint@8.22.0 + eslint-plugin-svelte3: 4.0.0_laaqauvsmoyypsiqkozwyi2fn4 fenceparser: 2.2.0 - fff-flavored-frontmatter: 0.2.1 + fff-flavored-frontmatter: 0.2.2 github-slugger: 1.4.0 mdast-util-to-string: 3.1.0 mdsvex: 0.10.6_svelte@3.49.0 @@ -98,16 +98,16 @@ devDependencies: shiki-twoslash: 3.1.0 svelte: 3.49.0 svelte-bricks: 0.1.7 - svelte-check: 2.8.0_vylzxgme5yisu3bsyvcau4hjtq + svelte-check: 2.8.1_vylzxgme5yisu3bsyvcau4hjtq svelte-preprocess: 4.10.7_fje22ktja5v2dh6nbkissncqme svelte-typeahead: 4.2.4 - tailwindcss: 3.1.8 + tailwindcss: 3.1.8_postcss@8.4.16 tslib: 2.4.0 typescript: 4.7.4 unist-util-visit: 4.1.0 - unocss: 0.45.6_vite@3.0.7 - vite: 3.0.7 - vite-plugin-pwa: 0.12.3_vite@3.0.7 + unocss: 0.45.7_vite@3.0.8 + vite: 3.0.8 + vite-plugin-pwa: 0.12.3_zz5rv25dunwhhp2yo7fftka3gi workbox-window: 6.5.4 packages: @@ -1482,7 +1482,7 @@ packages: resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} dev: true - /@rollup/plugin-babel/5.3.1_56fnebo2rl23pzm3cph57q7t7i: + /@rollup/plugin-babel/5.3.1_nacwgboicnu5wzmxlfrlauwase: resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} engines: {node: '>= 10.0.0'} peerDependencies: @@ -1495,36 +1495,36 @@ packages: dependencies: '@babel/core': 7.18.10 '@babel/helper-module-imports': 7.18.6 - '@rollup/pluginutils': 3.1.0_rollup@2.77.3 - rollup: 2.77.3 + '@rollup/pluginutils': 3.1.0_rollup@2.78.0 + rollup: 2.78.0 dev: true - /@rollup/plugin-node-resolve/11.2.1_rollup@2.77.3: + /@rollup/plugin-node-resolve/11.2.1_rollup@2.78.0: resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==} engines: {node: '>= 10.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0 dependencies: - '@rollup/pluginutils': 3.1.0_rollup@2.77.3 + '@rollup/pluginutils': 3.1.0_rollup@2.78.0 '@types/resolve': 1.17.1 builtin-modules: 3.3.0 deepmerge: 4.2.2 is-module: 1.0.0 resolve: 1.22.1 - rollup: 2.77.3 + rollup: 2.78.0 dev: true - /@rollup/plugin-replace/2.4.2_rollup@2.77.3: + /@rollup/plugin-replace/2.4.2_rollup@2.78.0: resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} peerDependencies: rollup: ^1.20.0 || ^2.0.0 dependencies: - '@rollup/pluginutils': 3.1.0_rollup@2.77.3 + '@rollup/pluginutils': 3.1.0_rollup@2.78.0 magic-string: 0.25.9 - rollup: 2.77.3 + rollup: 2.78.0 dev: true - /@rollup/pluginutils/3.1.0_rollup@2.77.3: + /@rollup/pluginutils/3.1.0_rollup@2.78.0: resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} engines: {node: '>= 8.0.0'} peerDependencies: @@ -1533,7 +1533,7 @@ packages: '@types/estree': 0.0.39 estree-walker: 1.0.1 picomatch: 2.3.1 - rollup: 2.77.3 + rollup: 2.78.0 dev: true /@rollup/pluginutils/4.2.1: @@ -1599,7 +1599,7 @@ packages: - supports-color dev: true - /@sveltejs/kit/1.0.0-next.405_svelte@3.49.0+vite@3.0.7: + /@sveltejs/kit/1.0.0-next.405_svelte@3.49.0+vite@3.0.8: resolution: {integrity: sha512-jHSa74F7k+hC+0fof75g/xm/+1M5sM66Qt6v8eLLMSgjkp36Lb5xOioBhbl6w0NYoE5xysLsBWuu+yHytfvCBA==} engines: {node: '>=16.9'} hasBin: true @@ -1608,18 +1608,18 @@ packages: svelte: ^3.44.0 vite: ^3.0.0 dependencies: - '@sveltejs/vite-plugin-svelte': 1.0.1_svelte@3.49.0+vite@3.0.7 + '@sveltejs/vite-plugin-svelte': 1.0.1_svelte@3.49.0+vite@3.0.8 chokidar: 3.5.3 sade: 1.8.1 svelte: 3.49.0 tiny-glob: 0.2.9 - vite: 3.0.7 + vite: 3.0.8 transitivePeerDependencies: - diff-match-patch - supports-color dev: true - /@sveltejs/vite-plugin-svelte/1.0.1_svelte@3.49.0+vite@3.0.7: + /@sveltejs/vite-plugin-svelte/1.0.1_svelte@3.49.0+vite@3.0.8: resolution: {integrity: sha512-PorCgUounn0VXcpeJu+hOweZODKmGuLHsLomwqSj+p26IwjjGffmYQfVHtiTWq+NqaUuuHWWG7vPge6UFw4Aeg==} engines: {node: ^14.18.0 || >= 16} peerDependencies: @@ -1637,7 +1637,7 @@ packages: magic-string: 0.26.2 svelte: 3.49.0 svelte-hmr: 0.14.12_svelte@3.49.0 - vite: 3.0.7 + vite: 3.0.8 transitivePeerDependencies: - supports-color dev: true @@ -1650,7 +1650,7 @@ packages: lodash.castarray: 4.4.0 lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 - tailwindcss: 3.1.8 + tailwindcss: 3.1.8_postcss@8.4.16 dev: true /@trysound/sax/0.2.0: @@ -1688,8 +1688,8 @@ packages: resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} dev: true - /@types/node/18.7.3: - resolution: {integrity: sha512-LJgzOEwWuMTBxHzgBR/fhhBOWrvBjvO+zPteUgbbuQi80rYIZHrk1mNbRUqPZqSLP2H7Rwt1EFLL/tNLD1Xx/w==} + /@types/node/18.7.6: + resolution: {integrity: sha512-EdxgKRXgYsNITy5mjjXjVE/CS8YENSdhiagGrLqjG0pvA2owgJ6i4l7wy/PFZGC0B1/H20lWKN7ONVDNYDZm7A==} dev: true /@types/pug/2.0.6: @@ -1699,13 +1699,13 @@ packages: /@types/resolve/1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: - '@types/node': 18.7.3 + '@types/node': 18.7.6 dev: true /@types/sass/1.43.1: resolution: {integrity: sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==} dependencies: - '@types/node': 18.7.3 + '@types/node': 18.7.6 dev: true /@types/trusted-types/2.0.2: @@ -1716,8 +1716,8 @@ packages: resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} dev: true - /@typescript-eslint/eslint-plugin/5.33.0_njno5y7ry2l2lcmiu4tywxkwnq: - resolution: {integrity: sha512-jHvZNSW2WZ31OPJ3enhLrEKvAZNyAFWZ6rx9tUwaessTc4sx9KmgMNhVcqVAl1ETnT5rU5fpXTLmY9YvC1DCNg==} + /@typescript-eslint/eslint-plugin/5.33.1_vsoshirnpb7xw6mr7xomgfas2i: + resolution: {integrity: sha512-S1iZIxrTvKkU3+m63YUOxYPKaP+yWDQrdhxTglVDVEVBf+aCSw85+BmJnyUaQQsk5TXFG/LpBu9fa+LrAQ91fQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -1727,12 +1727,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq - '@typescript-eslint/scope-manager': 5.33.0 - '@typescript-eslint/type-utils': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq - '@typescript-eslint/utils': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq + '@typescript-eslint/parser': 5.33.1_4rv7y5c6xz3vfxwhbrcxxi73bq + '@typescript-eslint/scope-manager': 5.33.1 + '@typescript-eslint/type-utils': 5.33.1_4rv7y5c6xz3vfxwhbrcxxi73bq + '@typescript-eslint/utils': 5.33.1_4rv7y5c6xz3vfxwhbrcxxi73bq debug: 4.3.4 - eslint: 8.21.0 + eslint: 8.22.0 functional-red-black-tree: 1.0.1 ignore: 5.2.0 regexpp: 3.2.0 @@ -1743,8 +1743,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser/5.33.0_qugx7qdu5zevzvxaiqyxfiwquq: - resolution: {integrity: sha512-cgM5cJrWmrDV2KpvlcSkelTBASAs1mgqq+IUGKJvFxWrapHpaRy5EXPQz9YaKF3nZ8KY18ILTiVpUtbIac86/w==} + /@typescript-eslint/parser/5.33.1_4rv7y5c6xz3vfxwhbrcxxi73bq: + resolution: {integrity: sha512-IgLLtW7FOzoDlmaMoXdxG8HOCByTBXrB1V2ZQYSEV1ggMmJfAkMWTwUjjzagS6OkfpySyhKFkBw7A9jYmcHpZA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1753,26 +1753,26 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 5.33.0 - '@typescript-eslint/types': 5.33.0 - '@typescript-eslint/typescript-estree': 5.33.0_typescript@4.7.4 + '@typescript-eslint/scope-manager': 5.33.1 + '@typescript-eslint/types': 5.33.1 + '@typescript-eslint/typescript-estree': 5.33.1_typescript@4.7.4 debug: 4.3.4 - eslint: 8.21.0 + eslint: 8.22.0 typescript: 4.7.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager/5.33.0: - resolution: {integrity: sha512-/Jta8yMNpXYpRDl8EwF/M8It2A9sFJTubDo0ATZefGXmOqlaBffEw0ZbkbQ7TNDK6q55NPHFshGBPAZvZkE8Pw==} + /@typescript-eslint/scope-manager/5.33.1: + resolution: {integrity: sha512-8ibcZSqy4c5m69QpzJn8XQq9NnqAToC8OdH/W6IXPXv83vRyEDPYLdjAlUx8h/rbusq6MkW4YdQzURGOqsn3CA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.33.0 - '@typescript-eslint/visitor-keys': 5.33.0 + '@typescript-eslint/types': 5.33.1 + '@typescript-eslint/visitor-keys': 5.33.1 dev: true - /@typescript-eslint/type-utils/5.33.0_qugx7qdu5zevzvxaiqyxfiwquq: - resolution: {integrity: sha512-2zB8uEn7hEH2pBeyk3NpzX1p3lF9dKrEbnXq1F7YkpZ6hlyqb2yZujqgRGqXgRBTHWIUG3NGx/WeZk224UKlIA==} + /@typescript-eslint/type-utils/5.33.1_4rv7y5c6xz3vfxwhbrcxxi73bq: + resolution: {integrity: sha512-X3pGsJsD8OiqhNa5fim41YtlnyiWMF/eKsEZGsHID2HcDqeSC5yr/uLOeph8rNF2/utwuI0IQoAK3fpoxcLl2g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -1781,22 +1781,22 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/utils': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq + '@typescript-eslint/utils': 5.33.1_4rv7y5c6xz3vfxwhbrcxxi73bq debug: 4.3.4 - eslint: 8.21.0 + eslint: 8.22.0 tsutils: 3.21.0_typescript@4.7.4 typescript: 4.7.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types/5.33.0: - resolution: {integrity: sha512-nIMt96JngB4MYFYXpZ/3ZNU4GWPNdBbcB5w2rDOCpXOVUkhtNlG2mmm8uXhubhidRZdwMaMBap7Uk8SZMU/ppw==} + /@typescript-eslint/types/5.33.1: + resolution: {integrity: sha512-7K6MoQPQh6WVEkMrMW5QOA5FO+BOwzHSNd0j3+BlBwd6vtzfZceJ8xJ7Um2XDi/O3umS8/qDX6jdy2i7CijkwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree/5.33.0_typescript@4.7.4: - resolution: {integrity: sha512-tqq3MRLlggkJKJUrzM6wltk8NckKyyorCSGMq4eVkyL5sDYzJJcMgZATqmF8fLdsWrW7OjjIZ1m9v81vKcaqwQ==} + /@typescript-eslint/typescript-estree/5.33.1_typescript@4.7.4: + resolution: {integrity: sha512-JOAzJ4pJ+tHzA2pgsWQi4804XisPHOtbvwUyqsuuq8+y5B5GMZs7lI1xDWs6V2d7gE/Ez5bTGojSK12+IIPtXA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -1804,8 +1804,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.33.0 - '@typescript-eslint/visitor-keys': 5.33.0 + '@typescript-eslint/types': 5.33.1 + '@typescript-eslint/visitor-keys': 5.33.1 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 @@ -1816,29 +1816,29 @@ packages: - supports-color dev: true - /@typescript-eslint/utils/5.33.0_qugx7qdu5zevzvxaiqyxfiwquq: - resolution: {integrity: sha512-JxOAnXt9oZjXLIiXb5ZIcZXiwVHCkqZgof0O8KPgz7C7y0HS42gi75PdPlqh1Tf109M0fyUw45Ao6JLo7S5AHw==} + /@typescript-eslint/utils/5.33.1_4rv7y5c6xz3vfxwhbrcxxi73bq: + resolution: {integrity: sha512-uphZjkMaZ4fE8CR4dU7BquOV6u0doeQAr8n6cQenl/poMaIyJtBu8eys5uk6u5HiDH01Mj5lzbJ5SfeDz7oqMQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: '@types/json-schema': 7.0.11 - '@typescript-eslint/scope-manager': 5.33.0 - '@typescript-eslint/types': 5.33.0 - '@typescript-eslint/typescript-estree': 5.33.0_typescript@4.7.4 - eslint: 8.21.0 + '@typescript-eslint/scope-manager': 5.33.1 + '@typescript-eslint/types': 5.33.1 + '@typescript-eslint/typescript-estree': 5.33.1_typescript@4.7.4 + eslint: 8.22.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0_eslint@8.21.0 + eslint-utils: 3.0.0_eslint@8.22.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys/5.33.0: - resolution: {integrity: sha512-/XsqCzD4t+Y9p5wd9HZiptuGKBlaZO5showwqODii5C0nZawxWLF+Q6k5wYHBrQv96h6GYKyqqMHCSTqta8Kiw==} + /@typescript-eslint/visitor-keys/5.33.1: + resolution: {integrity: sha512-nwIxOK8Z2MPWltLKMLOEZwmfBZReqUdbEoHQXeCpa+sRVARe5twpJGHCB4dk9903Yaf0nMAlGbQfaAH92F60eg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.33.0 + '@typescript-eslint/types': 5.33.1 eslint-visitor-keys: 3.3.0 dev: true @@ -1868,14 +1868,14 @@ packages: - supports-color dev: true - /@unocss/cli/0.45.6: - resolution: {integrity: sha512-U3Kl3i/UAIt2LXnBBcyNNnoF3qZV8y+tn4SNMa0P8iTgswKREzv8RuN5lGtdJohGQGoScKsNgj5t6WIKwts3lg==} + /@unocss/cli/0.45.7: + resolution: {integrity: sha512-ublLKCQiwfWOLayBksnzPbgHFRlRKT9ni6zJhbU5utyQpou9VT8n9OqxLgOZU1+PbPgg+KquJ2sdhgU5LtknMQ==} engines: {node: '>=14'} hasBin: true dependencies: - '@unocss/config': 0.45.6 - '@unocss/core': 0.45.6 - '@unocss/preset-uno': 0.45.6 + '@unocss/config': 0.45.7 + '@unocss/core': 0.45.7 + '@unocss/preset-uno': 0.45.7 cac: 6.7.12 chokidar: 3.5.3 colorette: 2.0.19 @@ -1885,128 +1885,128 @@ packages: perfect-debounce: 0.1.3 dev: true - /@unocss/config/0.45.6: - resolution: {integrity: sha512-evmhX/JT4SYsFOG5ora6To8PeJpBqWfCXZIEGFnmvjsUCVreacuLqwvxHtZZLpuwbIqY9VzzPdeuw3Ak2AZ/kg==} + /@unocss/config/0.45.7: + resolution: {integrity: sha512-qrkLLpZwQ5LhDRzJo7c4qzBYAcRdvc+A+53CdEmeROhkLIm+LSr0BpWqRIVXg5iifWNtWEpYYCxegVAm2/ZTng==} engines: {node: '>=14'} dependencies: - '@unocss/core': 0.45.6 + '@unocss/core': 0.45.7 unconfig: 0.3.5 dev: true - /@unocss/core/0.45.6: - resolution: {integrity: sha512-AeV2/HOTnmc2t2Z0oJ4qKhxDuvYKJ4sVLhqZi+Ex8SP5JSm65SrW3kmGWJMkSGHqJG6ey2ugAiZh6rHzELlB7A==} + /@unocss/core/0.45.7: + resolution: {integrity: sha512-eZVn+x/LM3rgMWeI95mGPzlIK1QDShuQMiycu2KJsMAdlVvzyZVPnpL+ATlNM1jeeL8G0iTNwPXMt8Lf8MxBDA==} dev: true - /@unocss/inspector/0.45.6: - resolution: {integrity: sha512-3Gx28OnCSf73OKlxKXs2JOvWqBeXgNSZdY9DaNZxj0yPFnNfTsdbyH16pk/ETV2Ab0xF+123oN0EI7SbFHYs0w==} + /@unocss/inspector/0.45.7: + resolution: {integrity: sha512-4dQBzg/k+Z+8p5qlM3NrOe1pgKZ3+zG6Ji8vbpPyYlfFQI1l2uqvCqGlry+AVUXfvmnWnILHIDDYYIM8bbSuRQ==} dependencies: gzip-size: 6.0.0 sirv: 2.0.2 dev: true - /@unocss/preset-attributify/0.45.6: - resolution: {integrity: sha512-BC9D7uH3cX8FKjDKzfLp94kjtkLqHbUwpcbA7naHfcsB79EGDXs35uRm8z9Br8zIXepbhBTbytM+6yE/8Kgq9g==} + /@unocss/preset-attributify/0.45.7: + resolution: {integrity: sha512-hso4834ggDjjtDx+6GgaS8wPyBKwNZB1KFpPkl3XyGr7FTW6vCR8kvHSN43iOAga85fcwC+4kQSh/is18Gm/uw==} dependencies: - '@unocss/core': 0.45.6 + '@unocss/core': 0.45.7 dev: true - /@unocss/preset-icons/0.45.6: - resolution: {integrity: sha512-9OD8q2PSS4hqECoVF7ZHUn/RRMFuNdSCAed/vvSrFFB4vLGKE6RQ4bO3RQqfi0L5SMyAT5FVqUEX+HsLQkwlbA==} + /@unocss/preset-icons/0.45.7: + resolution: {integrity: sha512-7vP4ayXvdlAa2JflDKH6CvzCIR+pQoiHml+TYEW+FYPP0UNz7lSImLwA7DWPotNYNZ0tFyx8VbaQLlViIwzyVA==} dependencies: '@iconify/utils': 1.0.33 - '@unocss/core': 0.45.6 + '@unocss/core': 0.45.7 ohmyfetch: 0.4.18 transitivePeerDependencies: - supports-color dev: true - /@unocss/preset-mini/0.45.6: - resolution: {integrity: sha512-hsneAcqqZTHN3tFXYIMsZMKgPoV1Ew4AFBn/dg3Z6/Bt1vIcLco/Iup1FhqjgvYm5Vdehszqb88VENZPV7CYjg==} + /@unocss/preset-mini/0.45.7: + resolution: {integrity: sha512-7qLJgc/wjRvjWpYDJb803zTEMpokSt3nTuzAw/0iSRbqaRpf2cvVObQQ+Ub8QzQO0rJSr1uj/AsgFbfJCEMeCw==} dependencies: - '@unocss/core': 0.45.6 + '@unocss/core': 0.45.7 dev: true - /@unocss/preset-tagify/0.45.6: - resolution: {integrity: sha512-Elv7fC/AoxA4EoUIZcqK0njMnwnpF/9amN+PDt93ZgK/GxD01vu7SsHNvupCsx2vIRzvJrr76gLSccA4+16e/g==} + /@unocss/preset-tagify/0.45.7: + resolution: {integrity: sha512-9c4x+hWTYmzXQHl7L05kVH7qEeE3CQJ6tdJNrudMHViuzUDVA6dN0QqQ5+0hO32Zg8r7wLAy71eam17rJKEWtg==} dependencies: - '@unocss/core': 0.45.6 + '@unocss/core': 0.45.7 dev: true - /@unocss/preset-typography/0.45.6: - resolution: {integrity: sha512-ZRsolR9ws37jLkKY88BYbBf3MWF82SGc7J+AWe/XDgaKoIN0T/9CJG7KLvW2AdtiVuTzBksLtbn/p9rYVgmjLA==} + /@unocss/preset-typography/0.45.7: + resolution: {integrity: sha512-3rAdNgMA6Dgdn4OQxBx7lstZ2dpQKBF+ETqMwmDtV0C/Q9QVqU4oP2xQOKKTLwUEzgfbhxaTKm7dZML8nSmcsQ==} dependencies: - '@unocss/core': 0.45.6 + '@unocss/core': 0.45.7 dev: true - /@unocss/preset-uno/0.45.6: - resolution: {integrity: sha512-4yFX1yqXSdz3yUXVBMHrDTqGmQMtvP4DZ1yY2tztg96rUhcNQS9zna/Z3fVSUnOMXjzPutJ0DfastkTpGDaFhA==} + /@unocss/preset-uno/0.45.7: + resolution: {integrity: sha512-Oj9qB1btfXKH0WXCf978yqZvm4hgWfycSBrQ0djsgaYLPC9jKn3DfMUAIjjrtLLHPE+WCTfWsKUoFAk+mWgtLQ==} dependencies: - '@unocss/core': 0.45.6 - '@unocss/preset-mini': 0.45.6 - '@unocss/preset-wind': 0.45.6 + '@unocss/core': 0.45.7 + '@unocss/preset-mini': 0.45.7 + '@unocss/preset-wind': 0.45.7 dev: true - /@unocss/preset-web-fonts/0.45.6: - resolution: {integrity: sha512-4dywnWm9tIYz/zu62/7DIjJ0Yzq9PViieJ1JZFZEz6iLbHlbCBkko51Hl+JAMQzlkwNdvVxQT7Y8jyzHWc1jow==} + /@unocss/preset-web-fonts/0.45.7: + resolution: {integrity: sha512-4h0BaGm9BMT7RHqBvd30G7sh7Nu/J97DdihZYFofpzLAgwca/RheL0PZNHsVIDQ7uWkp4mFt+UfdfYTBLeynRw==} dependencies: - '@unocss/core': 0.45.6 + '@unocss/core': 0.45.7 ohmyfetch: 0.4.18 dev: true - /@unocss/preset-wind/0.45.6: - resolution: {integrity: sha512-2W0l2z2zD5H7XU32B6PetOu5EA1FXh4CYp+p94XW79kD4oaohpGtabxgfPch68sOcbvQn/Cs4HaQNvkEBeTATA==} + /@unocss/preset-wind/0.45.7: + resolution: {integrity: sha512-E/uc2DqrgOqA7fs3qOiN3DmLeP7DTTAoPH9RGuTQPDT08jRaYd2k2vaoSbTv/PvZxAI2XvE9zWVqxtpFQheEdA==} dependencies: - '@unocss/core': 0.45.6 - '@unocss/preset-mini': 0.45.6 + '@unocss/core': 0.45.7 + '@unocss/preset-mini': 0.45.7 dev: true - /@unocss/reset/0.45.6: - resolution: {integrity: sha512-L02ur7LFzeIzOgQ419vA14498F76jbvKMeTdiVyR3Ym2C7BkxUGg6CgqY7Exm6e2qOjgcFkYU93aWaOyKi9hzw==} + /@unocss/reset/0.45.7: + resolution: {integrity: sha512-Gqx62iHO2hufpnS2vSm/NzCDG4UkF+XYwkKFarLzcj0UKCtOpQ9HCzSnZC422b3pM3lOvUIPpOOSncV6MUe5lg==} dev: true - /@unocss/scope/0.45.6: - resolution: {integrity: sha512-1x2ikRsHfDXHMRr68fU+cF55v0TILaGH+s5Us7swA1un1D70EdE8z1cU+RvY6WxWL8cQNb/aCvPf8daduKVCxg==} + /@unocss/scope/0.45.7: + resolution: {integrity: sha512-ly7feqPGuugr2nWMNSK8RUmwERr8Zzj1gy2xfhiN9UyYUafSPacHeQ7s9sdK4QHZcbtEqchiJdi7mNBhKPlb7w==} dev: true - /@unocss/transformer-attributify-jsx/0.45.6: - resolution: {integrity: sha512-QUdLZQI6+b9G94YjrN4e7JyPsbV7Eo+TYe5PFCA/awSn7nOBrb+/+WSKlSijbAfPaY8HgWXlnkOy1V0qpusn1w==} + /@unocss/transformer-attributify-jsx/0.45.7: + resolution: {integrity: sha512-FcRdJFmnR3o3RjnvYnwtbJ2sF85UvrvIpDa+HIgotZ4e9/9x8zCBIMgOxsgqWU5j+Zc9pvkD5dGEa7IQi+131w==} dependencies: - '@unocss/core': 0.45.6 + '@unocss/core': 0.45.7 dev: true - /@unocss/transformer-compile-class/0.45.6: - resolution: {integrity: sha512-D8q1bAua4QMbdOaJt6i+B21RssDJw42Gp/3tAR/v0ChnlcoBwDY4MPXbEY9j81dSeFIF+A7GkHAsb6oRaPcCfg==} + /@unocss/transformer-compile-class/0.45.7: + resolution: {integrity: sha512-yRCPPFrSfTG9+7e48KXP2P3DpJOGCLJX1N8nSg9HIPAwrKvIKldLqhGYojj0EBGbgPgwIgYrOlXBFDoAwH+bTA==} dependencies: - '@unocss/core': 0.45.6 + '@unocss/core': 0.45.7 dev: true - /@unocss/transformer-directives/0.45.6: - resolution: {integrity: sha512-nY+9wvpYeas997DkAhaCVdD2dauoqbgC66QYsCGxtEzBlNQXn3gmXT/CQgbYQuHknQCDwFuocWAH/f1Vmg3aIw==} + /@unocss/transformer-directives/0.45.7: + resolution: {integrity: sha512-BwNoixTG5p2VwnUHg9dbCe++0u2i0DPQ+c5X8iJjDBBFcU9uKiBZpw2YZX/RxYWAWLvfiCzC6VGsIKbEAdFs5Q==} dependencies: - '@unocss/core': 0.45.6 - css-tree: 2.2.0 + '@unocss/core': 0.45.7 + css-tree: 2.2.1 dev: true - /@unocss/transformer-variant-group/0.45.6: - resolution: {integrity: sha512-rVf/6ZgXwRqW1Xeia59ggk4L/9SAMUW0+slkm3mmvgn2fgJ0Pj4rzhLszRsHi3Oua0ZMbQoFXw+mY/GEEA6KfQ==} + /@unocss/transformer-variant-group/0.45.7: + resolution: {integrity: sha512-rxnu7YtjZXxghHTYCjgWIBerqp3u+FhionzAWQZ0Ull/i2rzMfDbQPOTpPvT+rYB6nJFNfOWYaJILmy/TKRejw==} dependencies: - '@unocss/core': 0.45.6 + '@unocss/core': 0.45.7 dev: true - /@unocss/vite/0.45.6_vite@3.0.7: - resolution: {integrity: sha512-6FcSoOWaciJCO0Bnhv001/9HTvO2ye1E/VX+Ey7JZg4ulltyhiszounRSxzBaflO2KDP/tE5O/K2iHywyHRrLg==} + /@unocss/vite/0.45.7_vite@3.0.8: + resolution: {integrity: sha512-nR4lUNGL6eV8YtrmgAA7IrccJaO9yT+5+W8KkSzIyWxZSITQL66OsOijv9ASdstyctqHjTHdJln6Xt850YWQ8w==} peerDependencies: vite: ^2.9.0 || ^3.0.0-0 dependencies: '@ampproject/remapping': 2.2.0 '@rollup/pluginutils': 4.2.1 - '@unocss/config': 0.45.6 - '@unocss/core': 0.45.6 - '@unocss/inspector': 0.45.6 - '@unocss/scope': 0.45.6 - '@unocss/transformer-directives': 0.45.6 + '@unocss/config': 0.45.7 + '@unocss/core': 0.45.7 + '@unocss/inspector': 0.45.7 + '@unocss/scope': 0.45.7 + '@unocss/transformer-directives': 0.45.7 magic-string: 0.26.2 - vite: 3.0.7 + vite: 3.0.8 dev: true /@vercel/nft/0.21.0: @@ -2166,7 +2166,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.21.3 - caniuse-lite: 1.0.30001375 + caniuse-lite: 1.0.30001378 fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -2177,7 +2177,7 @@ packages: /babel-plugin-dynamic-import-node/2.3.3: resolution: {integrity: sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==} dependencies: - object.assign: 4.1.3 + object.assign: 4.1.4 dev: true /babel-plugin-polyfill-corejs2/0.3.2_@babel+core@7.18.10: @@ -2264,8 +2264,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001375 - electron-to-chromium: 1.4.218 + caniuse-lite: 1.0.30001378 + electron-to-chromium: 1.4.222 node-releases: 2.0.6 update-browserslist-db: 1.0.5_browserslist@4.21.3 dev: true @@ -2309,13 +2309,13 @@ packages: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: browserslist: 4.21.3 - caniuse-lite: 1.0.30001375 + caniuse-lite: 1.0.30001378 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: true - /caniuse-lite/1.0.30001375: - resolution: {integrity: sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==} + /caniuse-lite/1.0.30001378: + resolution: {integrity: sha512-JVQnfoO7FK7WvU4ZkBRbPjaot4+YqxogSDosHv0Hv5mWpUESmN+UubMU6L/hGz8QlQ2aY5U0vR6MOs6j/CXpNA==} dev: true /chalk/2.4.2: @@ -2519,8 +2519,8 @@ packages: source-map: 0.6.1 dev: true - /css-tree/2.2.0: - resolution: {integrity: sha512-7y32czN0VBL8WkevhC/mrHnoHOmQaJ1Wvp8sjRuTz6/n9cjL83jQaUru2MvP7kzjpGVwrSy5CE4XyQObWGIHQQ==} + /css-tree/2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} dependencies: mdn-data: 2.0.28 @@ -2615,7 +2615,7 @@ packages: css-selector-tokenizer: 0.8.0 postcss: 8.4.16 postcss-js: 4.0.0_postcss@8.4.16 - tailwindcss: 3.1.8 + tailwindcss: 3.1.8_postcss@8.4.16 transitivePeerDependencies: - ts-node dev: true @@ -2659,8 +2659,8 @@ packages: resolution: {integrity: sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==} dev: true - /defu/6.0.0: - resolution: {integrity: sha512-t2MZGLf1V2rV4VBZbWIaXKdX/mUcYW0n2znQZoADBkGGxYL8EWqCuCZBmJPJ/Yy9fofJkyuuSuo5GSwo0XdEgw==} + /defu/6.1.0: + resolution: {integrity: sha512-pOFYRTIhoKujrmbTRhcW5lYQLBXw/dlTwfI8IguF1QCDJOcJzNH1w+YFjxqy6BAuJrClTy6MUE8q+oKJ2FLsIw==} dev: true /delegates/1.0.0: @@ -2762,8 +2762,8 @@ packages: jake: 10.8.5 dev: true - /electron-to-chromium/1.4.218: - resolution: {integrity: sha512-INDylKH//YIf2w67D+IjkfVnGVrZ/D94DAU/FPPm6T4jEPbEDQvo9r2wTj0ncFdtJH8+V8BggZTaN8Rzk5wkgw==} + /electron-to-chromium/1.4.222: + resolution: {integrity: sha512-gEM2awN5HZknWdLbngk4uQCVfhucFAfFzuchP3wM3NN6eow1eDU0dFy2kts43FB20ZfhVFF0jmFSTb1h5OhyIg==} dev: true /emoji-regex/8.0.0: @@ -2802,7 +2802,7 @@ packages: is-weakref: 1.0.2 object-inspect: 1.12.2 object-keys: 1.1.1 - object.assign: 4.1.3 + object.assign: 4.1.4 regexp.prototype.flags: 1.4.3 string.prototype.trimend: 1.0.5 string.prototype.trimstart: 1.0.5 @@ -3046,22 +3046,22 @@ packages: engines: {node: '>=10'} dev: true - /eslint-config-prettier/8.5.0_eslint@8.21.0: + /eslint-config-prettier/8.5.0_eslint@8.22.0: resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.21.0 + eslint: 8.22.0 dev: true - /eslint-plugin-svelte3/4.0.0_a7wk4ghvg4hia4trwaglu7p6cq: + /eslint-plugin-svelte3/4.0.0_laaqauvsmoyypsiqkozwyi2fn4: resolution: {integrity: sha512-OIx9lgaNzD02+MDFNLw0GEUbuovNcglg+wnd/UY0fbZmlQSz7GlQiQ1f+yX0XvC07XPcDOnFcichqI3xCwp71g==} peerDependencies: eslint: '>=8.0.0' svelte: ^3.2.0 dependencies: - eslint: 8.21.0 + eslint: 8.22.0 svelte: 3.49.0 dev: true @@ -3081,13 +3081,13 @@ packages: estraverse: 5.3.0 dev: true - /eslint-utils/3.0.0_eslint@8.21.0: + /eslint-utils/3.0.0_eslint@8.22.0: resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} peerDependencies: eslint: '>=5' dependencies: - eslint: 8.21.0 + eslint: 8.22.0 eslint-visitor-keys: 2.1.0 dev: true @@ -3101,8 +3101,8 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint/8.21.0: - resolution: {integrity: sha512-/XJ1+Qurf1T9G2M5IHrsjp+xrGT73RZf23xA1z5wB1ZzzEAWSZKvRwhWxTFp1rvkvCfwcvAUNAP31bhKTTGfDA==} + /eslint/8.22.0: + resolution: {integrity: sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: @@ -3116,7 +3116,7 @@ packages: doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.1.1 - eslint-utils: 3.0.0_eslint@8.21.0 + eslint-utils: 3.0.0_eslint@8.22.0 eslint-visitor-keys: 3.3.0 espree: 9.3.3 esquery: 1.4.0 @@ -3255,8 +3255,8 @@ packages: resolution: {integrity: sha512-fdXOPciCALTWvooKxyRUmYERiw1L1mzqVsfpk5F9kGr3NoT/Sdwew4BqGctY6xZpUXiZ7dXXPb27Om2nmUmHMg==} dev: true - /fff-flavored-frontmatter/0.2.1: - resolution: {integrity: sha512-e67ykDJbD00kqsBYvEZWMykcKDat1UtF0Vxi5CKhBad2V+FK3JGpqg8eUEtK7jao/UkPS0iyfKvSpZ67VXWQlQ==} + /fff-flavored-frontmatter/0.2.2: + resolution: {integrity: sha512-9o5OAYWuRn+g4ZAwje6kT1BAW/o2DdX0O66f/cJmDQehnAeZr7yzpl2Uu+UkUyReblhFL5XI2z73JgSaMpeBWw==} dev: true /file-entry-cache/6.0.1: @@ -3779,7 +3779,7 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.7.3 + '@types/node': 18.7.6 merge-stream: 2.0.0 supports-color: 7.2.0 dev: true @@ -4421,8 +4421,8 @@ packages: engines: {node: '>= 0.4'} dev: true - /object.assign/4.1.3: - resolution: {integrity: sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA==} + /object.assign/4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 @@ -4437,7 +4437,7 @@ packages: destr: 1.1.1 node-fetch-native: 0.1.4 ufo: 0.8.5 - undici: 5.8.2 + undici: 5.9.1 dev: true /once/1.4.0: @@ -5180,14 +5180,14 @@ packages: glob: 7.2.3 dev: true - /rollup-plugin-terser/7.0.2_rollup@2.77.3: + /rollup-plugin-terser/7.0.2_rollup@2.78.0: resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} peerDependencies: rollup: ^2.0.0 dependencies: '@babel/code-frame': 7.18.6 jest-worker: 26.6.2 - rollup: 2.77.3 + rollup: 2.78.0 serialize-javascript: 4.0.0 terser: 5.14.2 dev: true @@ -5206,6 +5206,14 @@ packages: fsevents: 2.3.2 dev: true + /rollup/2.78.0: + resolution: {integrity: sha512-4+YfbQC9QEVvKTanHhIAFVUFSRsezvQF8vFOJwtGfb9Bb+r014S+qryr9PSmw8x6sMnPkmFBGAvIFVQxvJxjtg==} + engines: {node: '>=10.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + /run-parallel/1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: @@ -5551,8 +5559,8 @@ packages: resolution: {integrity: sha512-2wJseuhlVYVRoCKJGQuwCySTMsvBVS4dchah6Ecz9RNng0zufx6NxToXc7iGf0QOHjo9wwAsOczRI+Ou2q4gEg==} dev: true - /svelte-check/2.8.0_vylzxgme5yisu3bsyvcau4hjtq: - resolution: {integrity: sha512-HRL66BxffMAZusqe5I5k26mRWQ+BobGd9Rxm3onh7ZVu0nTk8YTKJ9vu3LVPjUGLU9IX7zS+jmwPVhJYdXJ8vg==} + /svelte-check/2.8.1_vylzxgme5yisu3bsyvcau4hjtq: + resolution: {integrity: sha512-cibyY1sgt3ONIDnQbSgV2X9AJFhwEslRHNo95lijrYfPzVEvTvbmL2ohsUyqB5L7j1GhLXtQbjCJ4lZZ/fwbeQ==} hasBin: true peerDependencies: svelte: ^3.24.0 @@ -5670,10 +5678,12 @@ packages: stable: 0.1.8 dev: true - /tailwindcss/3.1.8: + /tailwindcss/3.1.8_postcss@8.4.16: resolution: {integrity: sha512-YSneUCZSFDYMwk+TGq8qYFdCA3yfBRdBlS7txSq0LUmzyeqRe3a8fBQzbz9M3WS/iFT4BNf/nmw9mEzrnSaC0g==} engines: {node: '>=12.13.0'} hasBin: true + peerDependencies: + postcss: ^8.0.9 dependencies: arg: 5.0.2 chokidar: 3.5.3 @@ -5839,12 +5849,12 @@ packages: resolution: {integrity: sha512-YMnPPUSfW0pT4Zzy4inM8tRHJZmhH+KcuFW/3qxkLKPhswEw18gQYe1jt57jY6ctFB0fnpiCpQ2Jtkbg4y/IPA==} dependencies: '@antfu/utils': 0.5.2 - defu: 6.0.0 + defu: 6.1.0 jiti: 1.14.0 dev: true - /undici/5.8.2: - resolution: {integrity: sha512-3KLq3pXMS0Y4IELV045fTxqz04Nk9Ms7yfBBHum3yxsTR4XNn+ZCaUbf/mWitgYDAhsplQ0B1G4S5D345lMO3A==} + /undici/5.9.1: + resolution: {integrity: sha512-6fB3a+SNnWEm4CJbgo0/CWR8RGcOCQP68SF4X0mxtYTq2VNN8T88NYrWVBAeSX+zb7bny2dx2iYhP3XHi00omg==} engines: {node: '>=12.18'} dev: true @@ -5926,31 +5936,31 @@ packages: engines: {node: '>= 10.0.0'} dev: true - /unocss/0.45.6_vite@3.0.7: - resolution: {integrity: sha512-ipU1kB2nbNvfh2O6u7qJ/zBwXvl/sRNlc+/dDWRCKu2feMnyR9g/4Vnw024RS8X+Jr7NN4eiGSzqMLoPoT6XOA==} + /unocss/0.45.7_vite@3.0.8: + resolution: {integrity: sha512-W0TpEKDXYS5pS8wwXpp4uavMVfqhzpcLB1zOlWTXQVnbo+zeAXsj9zNwiHjLyWNLVRYeoA9tjRLsVhAho+MLKg==} engines: {node: '>=14'} peerDependencies: - '@unocss/webpack': 0.45.6 + '@unocss/webpack': 0.45.7 peerDependenciesMeta: '@unocss/webpack': optional: true dependencies: - '@unocss/cli': 0.45.6 - '@unocss/core': 0.45.6 - '@unocss/preset-attributify': 0.45.6 - '@unocss/preset-icons': 0.45.6 - '@unocss/preset-mini': 0.45.6 - '@unocss/preset-tagify': 0.45.6 - '@unocss/preset-typography': 0.45.6 - '@unocss/preset-uno': 0.45.6 - '@unocss/preset-web-fonts': 0.45.6 - '@unocss/preset-wind': 0.45.6 - '@unocss/reset': 0.45.6 - '@unocss/transformer-attributify-jsx': 0.45.6 - '@unocss/transformer-compile-class': 0.45.6 - '@unocss/transformer-directives': 0.45.6 - '@unocss/transformer-variant-group': 0.45.6 - '@unocss/vite': 0.45.6_vite@3.0.7 + '@unocss/cli': 0.45.7 + '@unocss/core': 0.45.7 + '@unocss/preset-attributify': 0.45.7 + '@unocss/preset-icons': 0.45.7 + '@unocss/preset-mini': 0.45.7 + '@unocss/preset-tagify': 0.45.7 + '@unocss/preset-typography': 0.45.7 + '@unocss/preset-uno': 0.45.7 + '@unocss/preset-web-fonts': 0.45.7 + '@unocss/preset-wind': 0.45.7 + '@unocss/reset': 0.45.7 + '@unocss/transformer-attributify-jsx': 0.45.7 + '@unocss/transformer-compile-class': 0.45.7 + '@unocss/transformer-directives': 0.45.7 + '@unocss/transformer-variant-group': 0.45.7 + '@unocss/vite': 0.45.7_vite@3.0.8 transitivePeerDependencies: - supports-color - vite @@ -6027,16 +6037,17 @@ packages: vfile-message: 3.1.2 dev: true - /vite-plugin-pwa/0.12.3_vite@3.0.7: + /vite-plugin-pwa/0.12.3_zz5rv25dunwhhp2yo7fftka3gi: resolution: {integrity: sha512-gmYdIVXpmBuNjzbJFPZFzxWYrX4lHqwMAlOtjmXBbxApiHjx9QPXKQPJjSpeTeosLKvVbNcKSAAhfxMda0QVNQ==} peerDependencies: vite: ^2.0.0 || ^3.0.0-0 + workbox-window: ^6.4.0 dependencies: debug: 4.3.4 fast-glob: 3.2.11 pretty-bytes: 6.0.0 - rollup: 2.77.3 - vite: 3.0.7 + rollup: 2.78.0 + vite: 3.0.8 workbox-build: 6.5.4 workbox-window: 6.5.4 transitivePeerDependencies: @@ -6044,8 +6055,8 @@ packages: - supports-color dev: true - /vite/3.0.7: - resolution: {integrity: sha512-dILhvKba1mbP1wCezVQx/qhEK7/+jVn9ciadEcyKMMhZpsuAi/eWZfJRMkmYlkSFG7Qq9NvJbgFq4XOBxugJsA==} + /vite/3.0.8: + resolution: {integrity: sha512-AOZ4eN7mrkJiOLuw8IA7piS4IdOQyQCA81GxGsAQvAZzMRi9ZwGB3TOaYsj4uLAWK46T5L4AfQ6InNGlxX30IQ==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true peerDependencies: @@ -6159,9 +6170,9 @@ packages: '@babel/core': 7.18.10 '@babel/preset-env': 7.18.10_@babel+core@7.18.10 '@babel/runtime': 7.18.9 - '@rollup/plugin-babel': 5.3.1_56fnebo2rl23pzm3cph57q7t7i - '@rollup/plugin-node-resolve': 11.2.1_rollup@2.77.3 - '@rollup/plugin-replace': 2.4.2_rollup@2.77.3 + '@rollup/plugin-babel': 5.3.1_nacwgboicnu5wzmxlfrlauwase + '@rollup/plugin-node-resolve': 11.2.1_rollup@2.78.0 + '@rollup/plugin-replace': 2.4.2_rollup@2.78.0 '@surma/rollup-plugin-off-main-thread': 2.2.3 ajv: 8.11.0 common-tags: 1.8.2 @@ -6170,8 +6181,8 @@ packages: glob: 7.2.3 lodash: 4.17.21 pretty-bytes: 5.6.0 - rollup: 2.77.3 - rollup-plugin-terser: 7.0.2_rollup@2.77.3 + rollup: 2.78.0 + rollup-plugin-terser: 7.0.2_rollup@2.78.0 source-map: 0.8.0-beta.0 stringify-object: 3.3.0 strip-comments: 2.0.1 diff --git a/src/routes/2022-03-03.md b/src/routes/2022-03-03.md new file mode 100644 index 00000000..e62ea44f --- /dev/null +++ b/src/routes/2022-03-03.md @@ -0,0 +1,78 @@ +--- +title: JavaScript · 判断水仙花数 +summary: 用JavaScript判断水仙花数 +created: 2022-03-03T15:07:14.533Z +categories: + - JavaScript +tags: + - JavaScript +slug: Narcissistic-number-in-JavaScript +lastmod: 2022-04-07T07:20:02.340Z +--- + +题目来源: [“如果”可以“重来” | 百度前端技术学园](http://ife.baidu.com/javascript/if&while.html#%E7%BC%96%E7%A0%81%E4%B8%89) + +## 题目 + +根据用户输入的数据,判断水仙花数(三位数),水仙花数是指一个 n 位数 (n≥3),它的每个位上的数字的 n 次幂之和等于它本身。 + +```html +<label>请输需要判断的水仙花数(三位数):</label> +<input type="text" /> +<br /> +<button>开始判断</button> +<script> + function numDaffodils(num) { + // 判断是否为水仙花数 + } +</script> +``` + +**需求说明** + +- 当点击 `开始判断` 按钮,就执行 `numDaffodils` 函数判断输入的数字是否为水仙花数. +- 如果是,就弹出提示框提示是水仙花数,如果不是,就提示不是水仙花数 +- 例如输入 153,`153=1* 1*1+5*5*5+3*3*3` , 是水仙花数,就提示 153 是水仙花数。 +- 请加入输入判断,必须输入数字,不能输入其他类型。 + +## 解法 + +```html +<label>请输需要判断的水仙花数(三位数):</label> +<input type="text" /> +<br /> +<button>开始判断</button> +<script> + const btn = document.querySelector('button') + + function numDaffodils() { + let num = document.querySelector('input').value + + //输入的是字符串 + // console.log(typeof num); =>string + + // 检查是否是有效数字 + if (num.startsWith('0') || num.length !== 3 || isNaN(num)) alert('请输入三位有效数字') + + // 拆分为数组 + let numArr = num.split('') + + //判断是不是水仙花数字! + const numCheck = numArr.reduce((acc, value) => acc + Math.pow(value, 3), 0) + + numCheck == num ? alert('是水仙花数 ✅') : alert('不是水仙花数❗️') + } + + btn.addEventListener('click', numDaffodils) +</script> +``` + +这里有一个坑:从 `<input type="text">` 获取输入内容[^1],因为 `type=text` ,所以输出的是 `string` 而不是 `number`,不能直接用`typeof ==='number'`判断输入的是不是数字 + +## 参考 + +- [learn/task2_3 简单水仙花.html · Homeuh/learn · GitHub](https://github.com/Homeuh/learn/blob/6ed2d79cd6abff09f981c0af21080c38b55b6ef2/out/artifacts/Web0_1_Web_exploded/Task_JS/task2_3%E7%AE%80%E5%8D%95%E6%B0%B4%E4%BB%99%E8%8A%B1.html) + +- [IFE/水仙花数.html · Yaomiaomu/IFE · GitHub](https://github.com/Yaomiaomu/IFE/blob/fed038d6c76b2bf62ee83d6539c927c6fa333b91/JAVASCRIPT/%E6%B0%B4%E4%BB%99%E8%8A%B1%E6%95%B0.html) + +[^1]: [HTML text input allow only numeric input](https://stackoverflow.com/questions/469357/html-text-input-allow-only-numeric-input) diff --git a/src/routes/2022-03-04-decbin.md b/src/routes/2022-03-04-decbin.md new file mode 100644 index 00000000..754dd785 --- /dev/null +++ b/src/routes/2022-03-04-decbin.md @@ -0,0 +1,76 @@ +--- +title: JavaScript · 十进制数转二进制 +summary: 用JavaScript将十进制数转二进制数 +created: 2022-03-04T14:57:48.683Z +draft: '' +tags: + - JavaScript +categories: + - JavaScript +lastmod: 2022-04-16T12:54:16.167Z +type: default +# changelogs: +# - tag: "20220308" +# summary: +# - 将`push` 改为`unshift` +# - 使用`padding`填充字符串 +# - 修改`binNumber.length >= binBit` +--- + +## 题目 + +来源:[“如果”可以“重来” | 百度前端技术学园](http://ife.baidu.com/javascript/if&while.html#:~:text=opens%20new%20window) + +验证工具:[在线进制转换 | 进制转换器 — 在线工具](https://www.sojson.com/hexconvert.html) + +### Task1 + +实现当点击转化按钮时,将输入的十进制数字转化为二进制,并显示在 `result` 的 `p` 标签内 + +### Task2 + +- 转化显示后的二进制数为 bin-bit 中输入的数字宽度,例如 `dec-number` 为 5 ,`bin-bit` 为 5 ,则转化后数字为 `00101` +- 如果 `bin-bit` 小于转化后的二进制本身位数,则使用原本的位数,如 `dec-number` 为 5 ,`bin-bit` 为 2 ,依然输出 `101` ,但同时在 console 中报个错。 + +## 解法 + +```html +<input id="dec-number" type="number" placeholder="输入一个十进制非负整数" /> +<input id="bin-bit" type="number" placeholder="输入转化后二进制数字位数" /> +<button id="trans-btn">转化为二进制</button> +<p id="result">运算结果</p> +<script> + /////// Task 1 + const btn = document.querySelector('#trans-btn') + const result = document.querySelector('#result') + + function dec2bin() { + let decNumber = Number(document.querySelector('#dec-number').value) + + // 判断输入必须为一个非负整数 + if (decNumber < 0 || !Number.isInteger(decNumber)) { + alert('请输入一个非负整数!') + } + + // 求余 + let bin = [] + let remainder + while (decNumber !== 0) { + remainder = decNumber % 2 + decNumber = parseInt(decNumber / 2) + bin.unshift(remainder) + } + let binNumber = bin.join('') + + ////// Task2 + let binBit = Number(document.querySelector('#bin-bit').value) + if (binNumber.length >= binBit) { + binNumber = binNumber.slice(0, binBit + 1) + } else { + binNumber = binNumber.padStart(binBit, '0') + } + result.innerHTML = `运算结果:${binNumber}` + } + btn.addEventListener('click', dec2bin) +</script> +``` diff --git a/src/routes/2022-03-06-airtable.md b/src/routes/2022-03-06-airtable.md new file mode 100644 index 00000000..97e63f81 --- /dev/null +++ b/src/routes/2022-03-06-airtable.md @@ -0,0 +1,115 @@ +--- +title: Airtable · 网页剪藏 +summary: Airtable Web Cilpper设置 +created: 2022-03-06T05:58:29.026Z +categories: + - 实用技巧 +tags: + - 实用技巧 +# layout: post +lastmod: 2022-04-16T12:54:20.049Z +--- + +## 书签这回事 + +上回说到用 [自建网页书签 Flare](https://seviche.cc/blog/flare/) ,今天不小心把 SSH 链接弄坏了(也就是连不上了),因为搭载的服务不多,所以把整个服务器都重装了,Flare 网页书签也炸了。 + +其实搭建之后我没有用过(一次都没有),平时的书签管理主要靠搜索,各个浏览器之间的书签互相导入后,直接在搜索栏搜,如果是常用的网址,我用 Chorme 扩展 [eesel](https://chrome.google.com/webstore/detail/eesel-productivity-at-wor/jffaiidojfhflballoapgofphkadiono) 来解决,它可以列出最近用过的网页,按站点分类,查找起来很方便 + + + +最近还推出了一个新功能,可以通过命令进行一下快捷操作,如创建新的 coda 文件、figma 文件等……有点像 Alfred + + + +然后还有一个工具叫 [Omni](https://chrome.google.com/webstore/detail/omni-bookmark-history-tab/mapjgeachilmcbbokkgcbgpbakaaeehi) 可以做类似的事情,它还可以搜收藏夹,但不知道为什么我的 Chrome 用不了这个,所以也一直没用。 + +## 关于 Airtable + +Airtable 是一个多功能的表格应用,它的表格跟 Notion 里的 Database 挺像的,不过功能更多,用来做网页收藏夹 Free Plan 完全够用。基本的操作可以看这个: [真· Airtable 3 分钟菜鸟入门 - 少数派](https://sspai.com/post/44746) ,我没有什么要补充的。 + +从去年开始,我开始用 Airtable 整理我的一些收藏夹。原因如下: + +1. 可以分享的表格链接,移动端网页适配也很好 +2. 提供可嵌入网页(如博客)的 `<iframe>` 代码,样式也可以调整 +3. 方便的 Chrome 拓展,可以智能抓取网页标题和截图、描述 +4. 可以给收藏打 Tag,更好整理,也可以写补充描述/评分等 +5. 多种表格视图(Gallery/Calender/Kanban……) +6. 美丽: D + +当然这样做也有一些缺点,和其他专门做网页书签的应用不同,Airtable 只是一个「表格」,所以从表格到收藏的网页中去需要点两次,也就是需要打开条目再点一次链接。我把它定义为一个「收藏仓库」,而不是一个随用随取的「文具袋」,我会尽可能详细地描述收藏的条目,以备之后查找和辨识。 + +我的书签例子: + +<iframe title="coding resource" class="airtable-embed" src="https://airtable.com/embed/shrPHGWAGI8JypL16?backgroundColor=cyanLight&viewControls=on" frameborder="0" onmousewheel="" width="100%" height="533" style="background: transparent; border: 1px solid #ccc;"></iframe> + +## 怎么用 Airtable 剪切网页 + +我的收藏夹示例: [Airtable - About Coding](https://airtable.com/shrpftxf6JgRomP2X/tblEvtThXHNBMQ8lW/viwSXGTALloahC10H) + +### 1. 创建表格 + +至少包含三项内容: + +1. URL:用来放网页的链接 +2. LongText:网页描述 +3. Attachment:放网页截图 + +如果需要打开 Markdown 格式支持,需要打开 `Enable rich text formatting` + + +也可以增加 Tag 和 Categories 分类等其他内容,下面是我建的示例文件: + + + +### 2. 创建 app + +点击右上角的 `App` → 点击 `App an app` → 搜 `Web clipper` + + +点击 `add` 添加应用 + + + +然后按提示安装 Chrome 拓展,你可以直接在这里安装: [Airtable web clipper](https://chrome.google.com/webstore/detail/airtable-web-clipper/fehcbmngdgagfalpnfphdhojfdcoblgc) + +为剪切动作命名,如直接用表格名字:About Coding + + +然后点击 `Add to Extension`, 你会看到它出现在了 Web clipper 里面,不过现在先不用管,点击左上角关掉。 + +### 3. 配置剪切设置 + +在 Web clipper 的设置页面(如下),可以调整表格里面各个单元格对应的网页数据,可以按需设置 + + + +其中: + +- Page Title:页面标题 +- Page URL:页面链接 +- Selected text:打开 Web Clipper 时选中的文本 +- Meta tag:The field will be prefilled with the value of the matching meta tag. (不知道是什么 +- Text content by CSS selector:用 CSS 选择文本,会返回第一个符合选择器的文本内容,如 `.page-description` +- HTML attribute by CSS selector :结合 HTML 属性选择 + +我的设置是: + +- Name——Page title +- URL——Page URL +- Attachments——none +- Description——Selected Text + +### 4. Web Clipper 剪切 + +配置好后就可以开始使用了。在你需要剪切的网页,打开 Airtable web clipper,也就是先前安装的浏览器拓展,点击相应动作,比如刚才创建的 About Coding(如果这个面板有挡到页面内容,可以用鼠标拖动到别的地方) + + + +在 Attachment 里选择附加图片的来源: + + +Description 里面的内容可以自己写,也可以在打开 Web clipper 之前先选中,打开后会自动填充进去,如图: + + +最后点击 `Add record` 就完成啦 diff --git a/src/routes/2022-03-07-filter.md b/src/routes/2022-03-07-filter.md new file mode 100644 index 00000000..29415c7c --- /dev/null +++ b/src/routes/2022-03-07-filter.md @@ -0,0 +1,65 @@ +--- +title: JavaScript · 字符串去重 +summary: 编码实现字符串去重 +created: 2022-03-07T13:55:21.090Z +tags: + - JavaScript +categories: + - JavaScript +lastmod: 2022-04-07T07:20:30.550Z +--- + +## 题目 + +来源:[百度前端学院](http://ife.baidu.com/javascript/string.html#%E5%AD%97%E7%AC%A6%E4%B8%B2) + +```js +/* +去掉字符串 str 中,连续重复的地方 +*/ +function removeRepetition(str) { + // do something +} + +// 测试用例 +console.log(removeRepetition('aaa')) // ->a +console.log(removeRepetition('abbba')) // ->aba +console.log(removeRepetition('aabbaabb')) // ->abab +console.log(removeRepetition('')) // -> +console.log(removeRepetition('abc')) // ->abc +``` + +## 解法 + +```js +function removeRepetition(str) { + let strArr = [...str] + const result = strArr.filter((s, i, arr) => s !== arr[i + 1]).join('') + return result +} + +console.log(removeRepetition('aaa')) // ->a +console.log(removeRepetition('abbba')) // ->aba +console.log(removeRepetition('aabbaabb')) // ->abab +console.log(removeRepetition('')) // -> +console.log(removeRepetition('abc')) // ->abc +``` + +如果没有限定条件说是“连续重复”,就可以用 **Set**: + +```js +function removeRepetition(str) { + let strArr = [...new Set(str)] + return strArr.join('') +} +console.log(removeRepetition('aaa')) // ->a +console.log(removeRepetition('abbba')) // ->ab +console.log(removeRepetition('aabbaabb')) // ->ab +console.log(removeRepetition('')) // -> +console.log(removeRepetition('abc')) // ->abc +``` + +## 其他解法 + +- [filter 结合 call Method](https://www.programminghunter.com/article/7794242622/) +- [用 for 循环的两种方式](https://www.cnblogs.com/zyc-zsxbh/p/9327364.html) diff --git a/src/routes/2022-03-07-obsidian-notes1.md b/src/routes/2022-03-07-obsidian-notes1.md new file mode 100644 index 00000000..2730b953 --- /dev/null +++ b/src/routes/2022-03-07-obsidian-notes1.md @@ -0,0 +1,101 @@ +--- +title: Obsidian · 网课学习笔记整理 +summary: 拆分整合的过程 +created: 2022-03-06T16:23:33.118Z +tags: + - Obsidian +categories: + - Obsidian +lastmod: 2022-04-07T07:20:39.933Z +--- + +最近在用 obsidian 做网课学习笔记,感觉还挺好用的。简单记一下我记笔记的一些方法(其实也不算什么方法)需要用到的插件:Image Auto Upload,用来传图片。 + +其实我的记笔记方法很简单,就是不断拆碎重组,方便后面查找。 + +上课时,先按时间顺序书写笔记,就像传统的笔记本一样,上完课后再将那一页笔记拆碎重组到知识结构中。方法论大概是 MOC?就是用索引去整理笔记结构,而不是所处文件夹的层次,这里我们先不做深入探讨。 + +下面以学习 JavaScript 为例子。 + +我近期的笔记目录页面(用 Logseq 发布):[JavaScript](https://javascript-logseq.netlify.app/#/page/javascript) + +## 具体的方法 + +我把几乎所有的笔记都放在一个叫 `Zone` 的文件夹内,常用的会打上星标,或者移到最外层文件夹,新笔记默认放在 `Zone` 文件夹下。 + +### Step1-构建地图 + +MOC 是 Map of Contents,也就是内容地图,所以我们会从构建一张地图出发。刚开始地图不需要太完美,很精细,因为一个不识路的人是没办法认路的,何况是指路、画地图,反正后面也要调整,可以随意一点。 + +我刚开始创建了一个叫 `JavaScript` 的索引页,里面用标题列了几项比较重要的内容,比如 OOP / DOM 之类的,然后在页面最上面列了几项常用的内容: + + +之后会以这一页内容为目录索引,不断补充和修改,构建自己的知识结构 + +### Step2-写课堂笔记 + +首先需要创建一个空白页面。我用 Obsidian 里自带的插件 `ZK 卡片 ` 创建,可以自动生成时间戳标题,这个功能可以在设置里打开: + + + +然后点击左边功能栏就可以创建并打开了 + + + +创建好之后,把这页笔记添加到索引页中,方便后面查找: + + + +然后就可以写课堂笔记了,如果需要在笔记中插入图片,可以使用 Image Auto Upload 这个插件,配合 PicGo 客户端,可以在 Obsidian 里上传图片到图床,非常好用,直接粘贴图片到页面就可以了,具体可以看插件描述。 + + + +记笔记的过程没什么特别的,如果提到了一些我还不了解,以后还想深入的话题,我会用 `[[ ]]` 先标出来, 后面整理笔记的时候看到会留意下。 + +### Step3-重组笔记 + +做完笔记后,将笔记重组。 + +#### 布局 + +先打开三个窗口,布局如下: + + + +其中课堂笔记和索引页面需要锁定,这样新打开的窗口就会一直在右下角那个地方,将在这个区域编辑笔记内容。 + +#### 结构编辑 + +浏览课堂笔记大纲,看下本节课的知识点应该放在索引里的哪里,知识点之间应该是怎样的关系,在索引里用 `[[]]` 都列出来,简而言之就是画思维导图。我的一个比较粗糙的整理: + + + +这样就可以比较直观地看到哪些内容整理了,哪些没有整理。 + +#### 拆分笔记 + +然后就可以将左边的笔记拆分整合到右边的索引中了,按住快捷键 `CMD`,鼠标点击索引里的链接打开新页面,然后在右下部分复制整理。写完一个知识点后可以不用关闭窗口,按住 `CMD` 然后点击链接,继续在右下窗口编辑笔记。 + +看到索引浅色链接(没有创建页面的)都没了,就基本整理完了,可以再看看课程笔记里有没有要补充的。 + +然后就整理完啦!之后继续补充索引页面就好了……^\_^ + +<!-- ## 其他 + +有空再补充咯! + +### image auto upload + +<iframe src="https://social.datalabour.com/@nonsense/107907119934568990/embed" class="mastodon-embed" style="max-width: 100%; border: 0" width="100%" allowfullscreen="allowfullscreen"></iframe><script src="https://social.datalabour.com/embed.js" async="async"></script> + +### Copy Block Link + +<iframe src="https://social.datalabour.com/@nonsense/107831022090709742/embed" class="mastodon-embed" style="max-width: 100%; border: 0" width="100%" allowfullscreen="allowfullscreen"></iframe><script src="https://social.datalabour.com/embed.js" async="async"></script> + +### MOC + +<iframe src="https://o3o.ca/@nonsense/107642980121444003/embed" class="mastodon-embed" style="max-width: 100%; border: 0" width="100%" allowfullscreen="allowfullscreen"></iframe> + +### 随机发言 + +<iframe src="https://social.datalabour.com/@nonsense/107824733009904426/embed" class="mastodon-embed" style="max-width: 100%; border: 0" width="100%" allowfullscreen="allowfullscreen"></iframe><script src="https://social.datalabour.com/embed.js" async="async"></script> --> diff --git a/src/routes/2022-03-09-caesar-cipher.md b/src/routes/2022-03-09-caesar-cipher.md new file mode 100644 index 00000000..e4b07a61 --- /dev/null +++ b/src/routes/2022-03-09-caesar-cipher.md @@ -0,0 +1,116 @@ +--- +title: JavaScript · Caesar Cipher 凯撒加密 +summary: 用JavaScrpit编码实现凯撒加密算法 +created: 2022-03-08T16:01:08.850Z +tags: + - JavaScript +categories: + - JavaScript +lastmod: 2022-04-07T07:20:47.694Z +--- + +## 题目 + +来源:[操作字符串对象 | 百度前端技术学园](http://ife.baidu.com/javascript/string.html#%E5%AD%97%E7%AC%A6%E4%B8%B2) + +编码实现凯撒加密算法,根据输入的偏移量,实现对字符串的加密和解密. + +恺撒加密(Caesar cipher),是一种最简单且最广为人知的替换加密技术。明文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。 + +例如,当偏移量是 3 的时候,所有的字母 A 将被替换成 D,B 变成 E,以此类推。 + +**需求说明** + +- 点击加密按钮,根据用户输入的偏移量,对明文进行加密,加密后的为密文,显示在密文输入框中 +- 点击解密按钮,根据用户输入的偏移量,对密文进行加密,解密出来的为明文,显示在明文输入框中 + +## 解法 + +- `string.replace` 替换数字 +- 用`.charCodeAt()` 获取字母编码 +- 正则表达式`/[A-Za-z]/g`选择字母 +- 偏移量超过范围的,往反方向偏移 +- 结果验证:[凯撒密码加密/解密 - 一个工具箱 ](http://www.atoolbox.net/Tool.php?Id=778) + +### HTML + +```html +<label>偏移:</label> +<input type="text" name="offset" size="5" value="3" /> +<br /> +<label> + 明文: + <label></label> + <input type="text" name="plain" size="50" value="This is a test." /> + <br /> + <label>密文:</label> + <input type="text" name="enc" size="50" /> + <br /> + <input type="button" value="加密" onClick="encrypt()" /> + + <input type="button" value="解密" onClick="decrypt()" /> +</label> +``` + +### JS + +```js +let offsetInput = document.querySelector('input[name=offset]') +let plain = document.querySelector('input[name=plain') +let enc = document.querySelector('input[name=enc]') + +// 加密 +function encrypt() { + let offset = Number(offsetInput.value) + function conver(s) { + let charCode = s.charCodeAt(0) + // 替换大写字母 A-Z:65-90 + if (charCode <= 90 && charCode >= 65) { + return String.fromCharCode(charCode + offset < 90 ? charCode + offset : charCode - offset) + } else { + //替换小写字母 a-z:97-122 + return String.fromCharCode(charCode + offset < 122 ? charCode + offset : charCode - offset) + } + } + enc.value = plain.value.replace(/[A-Za-z]/g, conver) + + // 替换大写字母 A-Z:65-90 + // function transUpper(s) { + // let charCode = s.charCodeAt(); + // return String.fromCharCode( + // charCode + offset <= 90 ? charCode + offset : charCode - offset + // ); + // } + + // //替换小写字母 a-z:97-122 + // function transLower(s) { + // let charCode = s.charCodeAt(); + // return String.fromCharCode( + // charCode + offset <= 122 ? charCode + offset : charCode - offset + // ); + // } + // let encUpper = plain.replace(/[A-Z]/g, transUpper); + // enc.value = encUpper.replace(/[a-z]/g, transLower); +} + +// 解密 +function decrypt() { + let offset = Number(offsetInput.value) + function conver(s) { + let charCode = s.charCodeAt(0) + // 替换大写字母 A-Z:65-90 + if (charCode <= 90 && charCode >= 65) { + return String.fromCharCode(charCode - offset < 65 ? charCode + offset : charCode - offset) + } else { + //替换小写字母 a-z:97-122 + return String.fromCharCode(charCode - offset < 97 ? charCode + offset : charCode - offset) + } + } + plain.value = enc.value.replace(/[A-Za-z]/g, conver) +} +``` + +## 参考 + +- [凯撒密码 javascript](https://blog.csdn.net/dikanjiang6340/article/details/101264748?utm_relevant_index=1) +- [Caesar Cipher in Javascript - Stack Overflow](https://stackoverflow.com/questions/44232645/caesar-cipher-in-javascript?newreg=0a8ff4c05c484b01a7df20821475fb15) diff --git a/src/routes/2022-03-09-css-tab.md b/src/routes/2022-03-09-css-tab.md new file mode 100644 index 00000000..d3b72ed4 --- /dev/null +++ b/src/routes/2022-03-09-css-tab.md @@ -0,0 +1,101 @@ +--- +title: CSS · Tab选项卡 +summary: 一个纯 CSS 实现的 Tab 选项卡 +created: 2022-03-09T07:42:25.299Z +tags: + - CSS +categories: + - CSS +lastmod: 2022-04-07T07:40:02.371Z +--- + +一个纯 CSS 实现的 Tab 选项卡 + +## 原理 + +> 通过隐藏的 `input` 和与之关联的 [label](https://so.csdn.net/so/search?q=label&spm=1001.2101.3001.7020) 点击 `label` 触发 `input` 的 `checked` 状态触发的,再配合使用元素状态的伪类 `:checked `样式就可以实现不同状态的切换,中间的过度效果还可以配合 CSS3 的 `transition`过度效果实现 [^1]。 + +## 代码 + +- `input` 的`name` 都一样,`id`不同 + +### HTML + +```html +<div class="tab-frame"> + <!--标签页标题栏--> + <!-- 设置一个为check --> + <input type="radio" name="tab" id="tab1" check /> + <label for="tab1">TAB1</label> + + <input type="radio" name="tab" id="tab2" /> + <label for="tab2">TAB2</label> + + <input type="radio" name="tab" id="tab3" /> + <label for="tab3">TAB2</label> + + <!--Tab内容--> + <div class="tab-content"> + <p>THIS IS TAB1 CONTENT</p> + <p>Notice the gap between the content and tab after applying background cololr</p> + </div> + <div class="tab-content"> + <p>THIS IS TAB2 CONTENT</p> + <p>Notice the gap between the content and tab after applying background cololr</p> + </div> + <div class="tab-content"> + <p>THIS IS TAB3 CONTENT</p> + <p>Notice the gap between the content and tab after applying background cololr</p> + </div> +</div> +``` + +### CSS + +```css +/* 隐藏input和tab内容 */ +.tab-frame input, +.tab-content { + display: none; +} + +/* 导航栏样式:未选中时 */ +.tab-frame label { + color: #555; + padding: 10px 20px; + border-bottom: 1px solid #555; + cursor: pointer; + float: left; +} + +/* 导航栏样式:选中时 */ +.tab-frame input:checked + label { + color: #0f71aa; + border: 1px solid #555; + border-bottom: none; + border-radius: 4px 4px 0px 0px; + cursor: default; +} + +/* Tab内容样式 */ +.tab-frame .tab-content { + color: #0f71aa; + font-size: 1.5rem; + font-weight: bold; + padding-top: 40px; + clear: left; +} + +/* 点击的时候显示tab内容,即input checked的时候显示label*/ +.tab-frame input:nth-of-type(1):checked ~ .tab-content:nth-of-type(1), +.tab-frame input:nth-of-type(2):checked ~ .tab-content:nth-of-type(2), +.tab-frame input:nth-of-type(3):checked ~ .tab-content:nth-of-type(3) { + display: block; +} +``` + +## 参考 + +Demo:[Tabs CSS & HTML, no JS & Jquery](https://codepen.io/llgruff/pen/ZGBxOa) + +[^1]: [CSS tab 选项卡 (标签页) 切换](https://blog.csdn.net/baiding1123/article/details/51889201) diff --git a/src/routes/2022-03-09-typewriter.md b/src/routes/2022-03-09-typewriter.md new file mode 100644 index 00000000..4e4d3d33 --- /dev/null +++ b/src/routes/2022-03-09-typewriter.md @@ -0,0 +1,59 @@ +--- +title: JavaScript · 打字机效果生成器 +summary: 用 JavaScript 实现网页打字机效果 +created: 2022-03-08T16:19:05.137Z +tags: + - JavaScript +categories: + - JavaScript +lastmod: 2022-04-07T07:40:27.758Z +--- + +## 题目 + +来源:[百度前端学院](http://ife.baidu.com/javascript/string.html#%E4%BB%BB%E5%8A%A1%E5%9B%9B) + +参照 [打字机效果 DEMO (opens new window)](https://b.bdstatic.com/searchbox/icms/searchbox/img/%E6%89%93%E5%AD%97%E6%9C%BA.gif),实现一个打字机效果生成器 + +**需求说明** + +- 在输入框中输入需要实现打字机效果的文本 +- 实现原理使用定时器间隔一段时间递增地截取字符串的长度 +- 点击 button 实现打字机效果的生成,将文本输出到 id 为 showText 的标签中 + +```html +<label>请输入文本:</label> +<input type="text" /> +<button onclick="generateTypeEffect()">生成打字效果</button> +<h2 id="showText"></h2> +<script> + function generateTypeEffect() { + //这里实现打字机效果 + //将内容显示在h2中 + } +</script> +``` + +## 解法 + +```html +<label>请输入文本:</label> +<input type="text" /> +<button onclick="generateTypeEffect()">生成打字效果</button> +<h2 id="showText"></h2> +<script> + let i = 0 + function generateTypeEffect() { + const output = document.getElementById('showText') + const input = document.querySelector('input').value + if (i < input.length) { + output.textContent += input[i] + setTimeout(generateTypeEffect, 200, ++i) + } + } +</script> +``` + +## 参考 + +[How TO - Typing Effect](https://www.w3schools.com/howto/howto_js_typewriter.asp) diff --git a/src/routes/2022-03-10-forty.md b/src/routes/2022-03-10-forty.md new file mode 100644 index 00000000..d97b77d1 --- /dev/null +++ b/src/routes/2022-03-10-forty.md @@ -0,0 +1,30 @@ +--- +title: Forty页面仿写 +summary: 完成 HTML、CSS 代码编写,暂无 JavaScript +created: 2022-03-10T08:38:17.227Z +preview: '' +draft: '' +tags: + - CSS + - HTML +lastmod: 2022-04-07T07:39:48.473Z +--- + +## 题目 + +来源:[百度前端学院|浮动实战任务](http://ife.baidu.com/csspart/floatTask.html) + +通过 HTML 及 CSS 参考[示例图](https://b.bdstatic.com/searchbox/icms/searchbox/img/task1.png)实现页面开发,要求实现效果与示例图基本一致 + +- 页面宽度固定(定宽), 请应用 CSS 浮动以及前几天所学的 CSS 样式来完成页面效果 +- 只需要完成 HTML、CSS 代码编写,不需要写 JavaScript + +## 示例图 + + + +## Demo + +https://forty-seviche.netlify.app/ + +耗时:4 小时(还没有做自适应等很多东西……╮( ̄ ▽  ̄"")╭ diff --git a/src/routes/2022-03-11-miniflux-to-pocket.md b/src/routes/2022-03-11-miniflux-to-pocket.md new file mode 100644 index 00000000..9269cee6 --- /dev/null +++ b/src/routes/2022-03-11-miniflux-to-pocket.md @@ -0,0 +1,117 @@ +--- +title: Miniflux · 保存文章到 Pocket 以及 RSS +summary: 将 Miniflux 上的文章到保存到 Pocket/Instapaper,以及 RSS 相关文章和资源 +created: 2022-03-10T16:24:38.663Z +preview: '' +draft: '' +tags: + - RSS + - Miniflux +changelogs: + - tag: '202203011' + summary: + - 添加了`instapaper`的连接方式 + - 添加了Pocket按钮嵌入方式 +lastmod: 2022-04-07T07:38:52.406Z +--- + +将 Miniflux 上的文章到保存到 Pocket/Instapaper,以及 RSS 相关文章和资源 + +Miniflux 文档: [Integration with External Services](https://miniflux.app/docs/services.html) + +## 1. 创建 Pocket Application + +在 [这里](https://getpocket.com/developer/apps/new) 创建一个 Pocket 应用,以获取 Consumer Key + +我的设置如下: + + + +## 2. 获取 Consumer Key 用户密钥 + +在**My Apps**下面找到刚刚创建的应用,复制 Consumer Key: + + + +在 Miniflux 后台,设置 → 集成 → Pocket → **Pocket 用户密钥**(第一栏)中 填入刚刚复制的 Consumer key + +## 3. 获取 Access Token 访问密钥 + +填好后,通过通过点击下面的 **连接您的 Pocket 账户** 自动获取 Access Token(访问密钥): + + + +点击链接后按 **授权**。 + + +这里可能会跳到 `http://localhost/integration/pocket/callback` 然后就无法访问页面了,解决办法很简单,把 `localhost` 改为你的服务器 ip 端口或者 miniflux 所在域名即可,如 `http://miniflux.com/integration/pocket/callback`,按回车会跳回到 miniflux 设置页面。 + +出现这个提醒就连接成功了: + + +然后就可以点击文章页面的**保存**测试看看。 + + +## 其他 + +### 1.为博客添加 Pocket 收藏按钮 + +在 [此处](https://getpocket.com/publisher/button)复制需要的 Pocket 收藏按钮样式,添加到主题的 layout 里面(具体要看不同主题的设置,wordpress 似乎有内置这功能,我不确定,有三种效果。 + +### 2.用 Fever 同步到 Reeder + +1. 在 Miniflux 中创建 Fever 账户和密码 +2. 在 Reeder 中添加 Fever 账号,其中: + +- server:`https://miniflux 的网址/fever` +- email:Fever 用户名 +- password:就是 Fever 密码 + +### 3.连接到 Instapaper + +官网:[Instapaper](https://www.instapaper.com/) + +用户名为 Instapaper 的登录邮箱,设置好更新下就可以了~ + +## RSS 相关内容 + +来都来了,整理一下最近看过的相关内容,因为隐私问题,长毛象上的嘟文暂时不贴(除了我自己的 + +### 1. Miniflux 搭建 + +- [RSS | RSSHub 搭配 Miniflux,实现订阅自由](https://mantyke.icu/2021/rsshub-miniflux/) +- [Miniflux | 利用 Docker-compose 搭建 RSS 阅读器](https://blog.tantalum.life/posts/build-miniflux-in-docker/#%E6%90%AD%E5%BB%BA%E8%BF%87%E7%A8%8B) +- [Miniflux:自建私有 RSS 订阅工具,可多用户使用](https://www.moerats.com/archives/385/comment-page-1) +- [简易 RSS 阅读器 | Miniflux 2 安装教程](https://www.moewah.com/archives/3157.html) + +### 2. 其他选择 + +- [RSS 服务对比评测](https://type.cyhsu.xyz/2018/05/rss-aggregators-review-2018/) +- [找不到满意的 RSS 服务?你可以自己搭建一个](https://sspai.com/post/57498) +- [用 Tiny Tiny RSS 自建 RSS 服务](https://type.cyhsu.xyz/2017/10/use-ttrss-to-build-a-self-hosted-rss-service/) +- [(另一篇)Tiny Tiny RSS 教程](https://sspai.com/post/42787) +- [(2021) 自建 RSS 阅读器 Tiny Tiny RSS 教程,docker 安装 Awesome TTRSS](https://blog.naibabiji.com/tutorial/tiny-tiny-rss.html) +- [创建一个私有的 rss 订阅工具 Wallabag](https://www.vpslala.com/t/762) + +### 3. RSS 生成 + +- [可能是目前最全的 RSS 源,微信公众号也有!](https://mp.weixin.qq.com/s/K00wWvlAJu4KLbxru9-bXQ) +- [Feed Creator](http://createfeed.fivefilters.org/) +- [根据页面 HTML 生成 RSS 链接](https://social.datalabour.com/@nonsense/107824299041894067) +- [你的专属 RSS 源:在自己的 VPS 上安装 RSSHub](https://sspai.com/post/66451) +- [8 个好用的 WordPress RSS Feed 插件](https://www.wpdaxue.com/wordpress-rss-feed-plugins.html) + +### 4. 看什么 + +- [分享你认为值得订阅的内容 | Notion 小活动 Vol.13](https://www.notion.so/cnotion/Notion-Vol-13-89e51bdb621a4e009e7ec60d1cc58c2f#16368490755248a28efa4c229dc56321) +- [产品沉思录|优质订阅源](https://www.notion.so/ca290ef313804bae8584804440548c80?v=4470668a5078437f816b0273ed042ebf) +- [中文 Newsletter 导航](https://www.notion.so/kfang/Newsletter-68ee46c0a4574f659fb8a873ead438c6) +- [中文独立博客列表](https://github.com/timqian/chinese-independent-blogs) + +#### 5. 关于 RSS + +- [论 RSS 的「复兴」](https://type.cyhsu.xyz/2018/04/on-the-so-called-revival-of-rss/) +- [关于 RSS 协议的一些迷思](https://blog.dylanwu.space/2021/11/30/myth-of-rss.html) +- [是 RSS 复兴的时候了(翻译 )](https://www.fengkx.top/post/translation-of-RSS-revival/) +- [RSS3](https://rss3.io/) +- [拆解 RSS3— 是否可以真正的开启 Web3 社交?](https://mp.weixin.qq.com/s/CYmXvEHSd7idhDHtEe3ehw) diff --git a/src/routes/2022-03-29-12px.md b/src/routes/2022-03-29-12px.md new file mode 100644 index 00000000..08c2bd02 --- /dev/null +++ b/src/routes/2022-03-29-12px.md @@ -0,0 +1,52 @@ +--- +title: CSS · 解决 Chrome 中小于12px的字体不显示的问题 +lastmod: 2022-04-07T07:36:23.629Z +summary: 先用scale总体缩小再补上减少的宽度 +created: 2022-03-29T13:46:29.228Z +tags: + - CSS + - CSS Trick +categories: + - CSS +toc: false +--- + +如设置字体大小为 10.2px + +### HTML + +```html +<p> + I am a frontend developer with a particular interest in making things simple and automating daily tasks. I try to keep up with + security and best practices, and am always looking for new things to learn. +</p> +``` + +### CSS + +```css +p { + color: #dcdcdc; + + /*缩小基准大小,也就是缩小后的字体应该是 10.2px=12px*0.85*/ + font-size: 12px; + + /* 缩小比例 10.2px/12px=0.85 */ + transform: scale(0.85); + + /*设置缩放中心*/ + transform-origin: 0 0; + + /*(1-0.85)+1,补上缩小的宽度,这里可以按视觉效果调整一点*/ + width: 118%; + + /*兼容IE*/ + *font-size: 10.2px; +} +``` + +参考: + +- [css 小于 12px 字体\_MAIMIHO 的博客-CSDN 博客](https://blog.csdn.net/maimiho/article/details/121548769) +- [css 设置字体小于 12px 的方法 - 代码先锋网](https://www.codeleading.com/article/46263149244/) +- [Set CSS font-size less than 12px in webkit browser](https://codepen.io/mjj2000/pen/AYEqwJ) diff --git a/src/routes/2022-05-07-vps-init.md b/src/routes/2022-05-07-vps-init.md new file mode 100644 index 00000000..a2d4de09 --- /dev/null +++ b/src/routes/2022-05-07-vps-init.md @@ -0,0 +1,363 @@ +--- +title: VPS 安全初始化 +created: 2022-05-06 +summary: 上次 VPS 被别人暴力破解了,一哭二闹三重装之后,有了本文 +tags: + - VPS + - Self hosted +--- + +**前情提要:** + +前段时间我所购买的 VPS 服务商 Contabo 发邮件来说,我用 VPS 攻击了其他的服务器,让我快点停止这种行为,要是不改就罚我的钱,但是我并没有在上面装什么奇怪的东西,就只装了一个聊胜于无的 WordPress,手足无措之余在 Mastodon 哀嚎了一下,得到了很多热心网友的帮助,才发现原来我一直在裸奔使用 VPS,什么安全措施都没采取:( + +鉴于 VPS 上本来就没有什么东西,我决定重新初始化机子,本文是初始化的笔记,我的系统是 Ubuntu 20.04,文中提到的 ufw 是内置的,没有额外安装, 有些步骤上有所省略,最好对照着提到的参考文章看。 + +(再次感谢 Allen Zhong、糖喵、南狐、shrik3 等朋友的热心指导 o(≧v≦)o!) + +## 思路 + +下面这两点都是 Contabo 客服发给我的防护建议,用 Deepl 翻译了一下 + +### 日常防护 + +- 检查你的服务器是否有可疑的进程并删除它们(例如使用以下命令:ps aux| grep stealth) +- 检查错误日志,例如/var/log/apache2/error_log,找出是否有任何恶意脚本的错误信息或恶意软件下载的迹象。 +- 攻击者经常在以下目录中安装恶意软件。/tmp/ , /var/tmp/ - 请使用 find /tmp (find /var/tmp) 来检查隐藏的文件。 +- 扫描你的服务器以防止安装的 rootkits。 +- 运行一个防病毒软件,如 ClamAV 或 ClamWin。 + +### 安全检查 + +1. 保持定期备份。 +2. 保持你的整个系统一直是最新的,这意味着你必须定期安装使用的软件包和网络应用程序的更新和补丁。 +3. 安装并运行一个防病毒软件,如 ClamAV 或 ClamWin,以保持你的服务器不受恶意软件侵害。 +4. 设置一个防火墙,关闭所有你不需要的端口,并将 SSH 的 22 或 RDP 的 3389 等默认端口改为其他。 +5. 通过安装一个合适的软件,如 cPHulk 或 Fail2ban,阻止暴力攻击。 +6. 避免使用只在不安全的设置下工作的脚本。 +7. 不要点击电子邮件中的任何可疑附件或链接,或访问不安全的网站。 +8. 使用 SSH-Keys 而不是密码。 + +--- + +最后我将 VPS 里的内容全删了,从 0 出发,下面是具体的操作步骤: + +## 1. 创建新用户 + +参考: [VPS 建站新手上路 - YOLO](https://yolo.blue/vps-hosting-setup/) + +首先用 root 登陆,然后输入 adduser + 用户名 创建新用户,如添加用户`jack` + +```shell +adduser jack +``` + +接着输入两遍密码,其他信息可以按 <kbd>Enter </kbd>留空 + +给这个用户 root 权限: + +```shell +sudo usermod -aG sudo jack +``` + +其他参考: [如何在 Ubuntu 上添加和删除用户 | myfreax](https://www.myfreax.com/how-to-add-and-delete-users-on-ubuntu-18-04/) + +## 2. 配置 SSH-keys + +参考: [给 VPS 配置 SSH 密钥免密登录 - P3TERX ZONE](https://p3terx.com/archives/configure-ssh-keyfree-login-for-vps.html) + +### 本地生成 SSH 密钥对 + +文中提到可以在远端 VPS 上,也可以在本地,这里我选择在本地生成。 + +打开终端,输入 `ssh-keygen` ,连续按四次 <kbd>Enter </kbd>(密码设置为空),如果出现了 `overwrite(y/n)?` 就说明之前就有生成了,你可以选择 `y` 重新生成一个,或者就用已有的这个 + +比如: + +```bash +root@p3ter:~# ssh-keygen # 输入命令,按 Enter 键 +Generating public/private rsa key pair. +Enter file in which to save the key (/root/.ssh/id_rsa): # 保存位置,默认就行,按 Enter 键 +Enter passphrase (empty for no passphrase): # 输入密钥密码,按 Enter 键。填写后每次都会要求输入密码,留空则实现无密码登录。 +Enter same passphrase again: # 再次输入密钥密码,按 Enter 键 +Your identification has been saved in /root/.ssh/id_rsa. +Your public key has been saved in /root/.ssh/id_rsa.pub. +The key fingerprint is: +SHA256:GYT9YqBV4gDIgzTYEWFs3oGZjp8FWXArBObfhPlPzIk root@p3ter +The key's randomart image is: ++---[RSA 2048]----+ +|*OO%+ .+o | +|*=@.+++o. | +| *o=.=.... | +|. +.B + +o. | +| . + E *S. | +| o o | +| . | +| | +| | ++----[SHA256]-----+ +``` + +出现那个神秘的矩形就是生成好了 + +### 安装公钥 + +在本地终端: + +```bash +ssh-copy-id -pport user@remote +``` + +`user` 为用户名,`remote` 为 IP 地址,`port` 为端口号。 + +也可以不加端口号: + +```bash +ssh-copy-id user@remote +``` + +然后按要求输入密码,如果是用 root 登陆的,就是用的初始密码,如果是用上面设置的新用户,那就跟之前设置的用户密码一样 + +### 修改权限 + +**登入 VPS **后,在远程终端输入: + +```bash +chmod 600 .ssh/authorized_keys +``` + +### 修改 sshd 配置文件 + +打开配置文件: + +```bash +nano /etc/ssh/sshd_config +``` + +找到下面这两行,并改成这样: + +```bash +PermitRootLogin no +AllowUsers username #如果没有这一行就手动添加 +RSAAuthentication yes #这一行我找不到就没有配置 +PubkeyAuthentication yes +``` + +记得 username 要换成自己设置的名字,也就是上面配置的 jack + +修改完按 <kbd>Ctrl</kbd>+<kbd>o</kbd> 保存,<kbd>Enter</kbd> 确认,<kbd>Ctrl</kbd>+<kbd>X</kbd> 退出编辑 + +重启 ssh 服务 + +```bash +systemctl reload sshd +``` + +或者 + +```bash +service sshd restart +``` + +### 禁用密码登陆和改端口 + +设置好后,试试看能不能用 ssh 登陆,如果可以,再 `sudo nano /etc/ssh/sshd_config` 修改配置,禁用密码登陆: + +```bash +PasswordAuthentication no +``` + +### 修改默认登陆端口 + +然后改默认登陆端口[^1],应该什么数都可以吧,什么 8080,9080,8888,3141…… + +找到 `Port 22` 这行,在下面加你要开的口 + +```bash + Port 22 + Port 8888 +``` + +加完了之后重启 + +```bash +sudo service sshd restart +``` + +打开防火墙并给你设置的端口放行 + +```bash +sudo ufw allow 8888 +sudo ufw enable +``` + +`sudo ufw status` 查看防火墙状态,比如: + +```bash +Status: active + +To Action From +-- ------ ---- +8888 ALLOW Anywhere +8888 (v6) ALLOW Anywhere (v6) +``` + +然后重新连接一下 VPS,用设置好的端口登陆看看,如果没问题的话重新 `sudo nano /etc/ssh/sshd_config` ,注释掉 `Port 22` 那一行 + +## 3. 安装 ClamAV + +参考: + +- [如何在 Ubuntu 20.04 LTS 上安装 ClamAV - LinuxCapable](https://www.linuxcapable.com/zh-CN/%E5%A6%82%E4%BD%95%E5%9C%A8-ubuntu-20-04-%E4%B8%8A%E5%AE%89%E8%A3%85%E5%92%8C%E4%BD%BF%E7%94%A8-clamav/) +- [How to Install and Use ClamAV on Ubuntu 20.04](https://linoxide.com/how-to-install-and-use-clamav-on-ubuntu-20-04/) + +### 安装 + +```bash +sudo apt update +sudo apt install clamav clamav-daemon -y +``` + +### 更新病毒数据库 + +先停止 `clamav-freshclam` 服务 + +```bash +sudo systemctl stop clamav-freshclam +``` + +执行更新: + +```bash +sudo freshclam +``` + +启动`clamav-freshclam` 服务 + +```bash +sudo systemctl start clamav-freshclam +``` + +### 开机启动 + +```bash +sudo systemctl is-enabled clamav-freshclam +``` + +### 下载 ClamAV 数据库 + +先关掉 clamav-freshclam 再下载 + +```bash +sudo systemctl stop clamav-freshclam +sudo freshclam +``` + +查看 clamav 的目录和文件的日期 + +```bash +ls /var/lib/clamav/ +``` + +### 限制 Clamscan CPU 使用率 + +**`nice`**:降低 clamscan 的优先级(限制相对 cpu 时间)。 + +```bash +sudo nice -n 15 clamscan +``` + +**`cpulimit`**:限制绝对的 CPU 时间。 +安装 cpulimit + +```bash +sudo apt-get install cpulimit +``` + +使用 cpulimit 来限制 clamscan: + +```bash +cpulimit -z -e clamscan -l 20 & clamscan -ir / +``` + +### 常见 CLI + +```bash +clamscan /home/filename.docx #扫描特定目录或文件 +clamscan --no-summary /home/ #扫描结束时不显示摘要 +clamscan -i / #打印受感染的文件 +clamscan --bell -i /home #警惕病毒检测 +clamscan -r --remove /home/USER #删除受感染的文件 +``` + +### ClamAV 返回码 + +- 0:未发现病毒。 +- 1:发现病毒。 +- 2:发生了一些错误。 + +## 4. 安装 Fail2ban + +安装 fail2ban 以阻止重复登录尝试 + +参考:[准备你的机器 - Mastodon documentation](https://docs.joinmastodon.org/zh-cn/admin/prerequisites/) + +### 准备 + +更新软件包: + +```bash +sudo apt update +sudo apt upgrade -y +``` + +### 安装 + +参考:[如何在 Ubuntu 20.04 上安装和配置 Fail2ban](https://www.myfreax.com/install-configure-fail2ban-on-ubuntu-20-04/) + +```bash +sudo apt install fail2ban +``` + +安装完后将自动启动,可以用`sudo systemctl status fail2ban` 查看运行状态 + +### 修改配置: + +打开`/etc/fail2ban/jail.local`: + +```bash +sudo nano /etc/fail2ban/jail.local +``` + +写入下面的内容,修改邮箱,如果端口改了,也要记得相应修改 + +```text +[DEFAULT] +destemail = your@email.here +sendername = Fail2Ban + +[sshd] +enabled = true +port = 22 + +[sshd-ddos] +enabled = true +port = 22 +``` + +重启 fail2ban: + +```bash +sudo systemctl restart fail2ban +``` + +## 5. SSL 证书相关 + +还没弄明白怎么回事,待更 + +参考: + +- 【[杂谈】防止 SSL 证书泄露你的源站 IP](https://luotianyi.vc/5056.html) +- [WEB 服务器安全指南 - 防止源站 IP 暴露](https://blog.hicasper.com/post/114.html) + +[^1]: [更改 VPS 的默认 SSH 端口 22 – 托尼的博客](https://zhucaidan.xyz/2019/12/281/) diff --git a/src/routes/2022-05-25-git.md b/src/routes/2022-05-25-git.md new file mode 100644 index 00000000..effca0d9 --- /dev/null +++ b/src/routes/2022-05-25-git.md @@ -0,0 +1,87 @@ +--- +title: Git · 常用操作笔记 +created: 2022-05-25 +summary: 每次更新博客进行的操作以及常见错误处理 +tags: + - Git +--- + +**资料:** + +- [GIT CHEAT SHEET](https://education.github.com/git-cheat-sheet-education.pdf) +- [45 个 Git 经典操作场景,专治不会合代码](https://mp.weixin.qq.com/s/BzdgZXyM1UaNCUCXySL9Rw) +- [版本控制(Git) - 计算机教育中缺失的一课](https://missing-semester-cn.github.io/2020/version-control/) +- [战壕里面的 Git(Git In The Trenches)](http://cbx33.github.io/gitt/intro.html) + +## 每次更新博客进行的操作 + +### 1. 追踪所有文件 + +(除了 gitignore 里面的),也可以单独加 + +`git add -A` + +### 2. 提交上传信息 + +`git commit -m '一些信息,如fixed something etc'` + +### 3. push 到 Github + +`git push origin main` + +等待一会儿就好了,如果不行,换个网或者关掉 VPN 看看 + +## 常用 Git 操作 + +- `.gitignore`: 放不想传到 git repo 的文件/文件夹 +- 当内容改动很多的时候,最好开一个 branch +- VSCode 文件后面的字母: + - U:untrack + - M: modified + - A : on track +- 一般不在`main` 或`master` 修改代码,而是开一个 branch,确定好后再 merge +- 下载叫做 pull,上传是 push + +| 命令 | 作用 | +| :----------------------------------------------------------- | ------------------------------------------------- | +| `git config --global user.name 名字` | 设置名字 | +| `git config --global user.email 邮箱` | 设置邮件 | +| `git init` | 初始化 | +| `git add -A` | 追踪所有文件(除了 gitignore 里面的),也可以单独加 | +| `git commit -m` | m 代表信息,后面要写 commit 相关信息 | +| `git status` | 查看 git 状态/信息 | +| `git log` | 查看 commit 日志,按 Q 才可以退出 | +| `git reset hard (commit的id)` | 回到特定版本 | +| `git reset hard --HEAD` | 返回上一次改动 (还没有 commit) | +| `git branch` | 列出现在有的 branch,按 Q 退出 | +| `git branch (branch'name)` | 创建新 branch | +| `git merge (branch'name)` | 合并 branch 到 main | +| `git checkout (branch's name)` | 切换 branch | +| `git remote add origin https://github.com/用户名/仓库名.git` | 链接到 remote repo | +| `git pull` | 拉更新 | +| `git push origin (branch'name)` | push 到 remote repo | + +## 常见问题 + +下面是一些我看过的文章 + +### 版本回滚 + +- [项目中 git 怎么回退到之前的版本 & git 放弃本地修改,强制拉取更新](https://mp.weixin.qq.com/s/MCtCQg7rcokf6IrZVINF4w) +- [Git 学习笔记:版本回退](https://mp.weixin.qq.com/s/98wEvWU6OYVkPauWn-XXng) +- [如果你还不会用 git 回滚代码,那你一定要来看看](https://mp.weixin.qq.com/s/FPiSyeivTKhoAgJmORZFog) + +### 报错处理 + +- [git push 错误 failed to push some refs to 解决方法](https://blog.csdn.net/qq_39416311/article/details/102219428) +- [git 上传忽略 node_modules](https://blog.csdn.net/jiandan1127/article/details/81205530) + +### 博客相关 + +- [GitHub Pages 绑定来自阿里云的域名](https://blog.csdn.net/qq_29232943/article/details/52786603) +- [Hexo 发布到 Github 丢失 readme 和 CNAME 解决方案](https://www.cnblogs.com/LandWind/articles/8269636.html) +- [把 HUGO 博客托管到 GITHUB 上](https://www.freesion.com/article/37111127345/) + +### 其他 + +- [Github 上如何添加 LICENSE 文件?](https://www.cnblogs.com/chenmingjun/p/8555906.html) diff --git a/src/routes/2022-05-26-write-a-page-template.md b/src/routes/2022-05-26-write-a-page-template.md new file mode 100644 index 00000000..56da5280 --- /dev/null +++ b/src/routes/2022-05-26-write-a-page-template.md @@ -0,0 +1,300 @@ +--- +title: 为博客写一个Project showcase 页面 +created: 2022-05-26 +summary: 第一次Pull Request的经历 +tags: + - Svelte + - Open Source +--- + +这两天为博客写了一个 Project 的页面用来放我的作品,这里记录一下我是怎么写(模仿)的,我对 Svelte 语法的了解不多,没有特别深入学习,只是在官方看了下文档和用了下他们的 [交互式教程](https://www.sveltejs.cn/tutorial/basics) ,编码的过程是一边学习一边模仿慢慢摸索的,虽然最终没有 merge 到 repo 中,但我觉得整个过程都蛮兴奋的。 + +既然有了博客,那我肯定是要写一下这个过程的。 + +## 1. 分析需求 + +我想要的是一个独立的 Page,而不是一个 Post 页面,最后把它放在导航栏里面。 +想要有以下这几个功能: + +- 技术栈分类 +- 项目类别筛选 +- 项目展示 + +主要有这些信息的展示: + +- 项目标题 +- 项目图片 +- 项目描述 +- 技术栈 +- 项目类别 + +## 2. 画原型图 + +明确了需求后,参考了几个项目平台的布局,在 [Whimsical](https://whimsical.com/) 上画了原型图如下: + + +目前还没有做上面 Tag 的分类功能,之后可能会做吧 + +## 2. 创建组件样式 CSS + +为了统一风格,我在博客现有框架里四处搜寻可用的组件样式,想在这基础上修改,然后我找到了作者 藍 在 Tailwind Play 上的友链组件,感觉很适合,然后就直接在这个 Tailwind Play Demo 上进行了样式修改,不过此时填写的数据都是死数据,后面再进行修改。 + +因为我之前没有怎么用过 Tailwind,所以是一边对照 Tailwind 文档修改的,然后 Tailwind Play 上的代码提示功能真的很新手友好,hover CSS class 的时候会显示具体的 CSS 原始参数,很直观。 + + +最后我构建的 Demo 样式如下: +[Tailwind Play](https://play.tailwindcss.com/uQwYojgpuk?layout=horizontal) + + + +## 4. 编写组件代码 + +整个页面的构建跟 Friend 页面很像,我分析了 Friend 页面所涉及到的代码和结构,然后一点点模仿构建 Project 页面。 + +### 数据 + +首先根据需求确定传入的数据及其格式,以便后面使用 TypeScript 的提示 + +- 参考:`/src/lib/config/friends.ts` +- 新建:`/src/lib/config/projects.ts` + +```ts twoslash title="/src/lib/config/friends.ts" +export interface FriendOld { + // hCard+XFN + id: string // HTML id + rel?: string // XFN, contact / acquaintance / friend + link?: string // URL + html?: string // HTML + title?: string // 标题 + descr?: string // 描述 + avatar?: string // 头像 + name?: string // backwards compatibility +} + +export type Friend = { + id: string // HTML id + rel?: string // XHTML Friends Network + link?: string // URL + html?: string // Custom HTML + + title?: string // 标题 + name?: string // 人名 + avatar?: string // 头像 + descr?: string // 描述 + class?: { + avatar?: string // 头像类名 + img?: string // 图片类名 + } +} + +export const friends: Friend[] = [ + { + id: 'id', + rel: '', + title: '', + name: '', + link: '', + descr: '', + avatar: '' + } +] +``` + +```ts twoslash title="/src/lib/config/projects.ts" +export type Project = { + id: string + name?: string + tags?: string[] + feature?: string + description?: string + img?: string + link?: string +} + +export const projects: Project[] = [ + { + id: 'coach', + name: 'Find a Coach', + tags: ['Vue 3', 'Composition API'], + feature: 'Vue3', + description: + '既然如何, 问题的关键究竟为何? 要想清楚,科学和人文谁更有意义,到底是一种怎么样的存在。 普列姆昌德曾经提到过,希望的灯一旦熄灭,生活刹那间变成了一片黑暗。这启发了我, 那么, 我认为, 总结的来说,', + img: 'https://uneorange.oss-cn-guangzhou.aliyuncs.com/202205251801454.avif', + link: 'https://seviche.cc' + } +] +``` + +### 组件 + +将 CSS 复制进去,并注入数据 + +- 参考:`/src/lib/components/extra/friend.svelte` +- 新建:`/src/lib/components/extra/project.svelte` + +```html title="/src/lib/components/extra/friend.svelte" +<script lang="ts"> + import type { Friend } from '$lib/config/friends' + import Footer from '$lib/components/footer.svelte' + export let item: unknown + let friend = item as unknown as Friend +</script> + +{#if friend.id === 'footer'} +<footer rounded="{true}" class="p-4 md:p-8" /> +{:else if friend.html} +<a id="{friend.id}" rel="{friend.rel}" href="{friend.link}" class="h-card u-url">{@html friend.html}</a> +{:else} +<a + id="{friend.id}" + rel="{friend.rel}" + href="{friend.link}" + class="card bg-base-100 shadow-xl hover:shadow-2xl transition-shadow h-card u-url"> + <div class="absolute text-4xl font-bold opacity-5 rotate-6 leading-tight top-4">{friend.name ?? ''}{friend.title ?? ''}</div> + <div class="card-body p-4"> + <div class="flex items-center gap-4"> + {#if friend.avatar} + <div class="avatar {friend.class?.avatar} shrink-0 w-16 mb-auto"> + <img class="{friend.class?.img ?? 'rounded-xl'} u-photo" src="{friend.avatar}" alt="{friend.title}" /> + </div> + {:else} + <div class="avatar {friend.class?.avatar} placeholder mb-auto"> + <div class="{friend.class?.img ?? 'bg-neutral-focus text-neutral-content shadow-inner rounded-xl'} w-16"> + <span class="text-3xl">{(friend.name ?? friend.title).charAt(0)}</span> + </div> + </div> + {/if} + <div class="card-title flex-col gap-0 flex-1 items-end"> + <span class="text-right p-name">{friend.name ?? ''}</span> + <span class="opacity-50 text-right">{friend.title}</span> + </div> + </div> + {#if friend.descr} + <div class="prose opacity-70 p-note">{friend.descr}</div> + {/if} + </div> +</a> +{/if} +``` + +根据具体的页面效果修改了 Demo 中的 CSS 样式 + +```html title="/src/lib/components/extra/project.svelte" +<script lang="ts"> + import type { Project } from '$lib/config/projects' + import Footer from '$lib/components/footer.svelte' + export let item: unknown + let project = item as unknown as Project + let tags = project.tags +</script> + +{#if project.id === 'footer'} +<footer rounded="{true}" class="max-w-4xl mx-auto p-4 md:p-8" /> +{:else} +<a + id="{project.id}" + href="{project.link}" + class="card mx-auto max-w-4xl bg-base-100 shadow-xl transition-shadow mb-7 h-card u-url hover:shadow-2xl"> + <div class="absolute text-5xl font-bold opacity-5 rotate-6 leading-tight top-2 right-0">{project.feature}</div> + <div class="card-body p-4"> + <div class="flex flex-col md:flex-row items-start gap-4"> + <div class="mb-auto max-w-full shrink-0 md:max-w-xs"> + <img class="rounded-md" src="{project.img}" alt="{project.description}" /> + </div> + <div class="card-title flex-1 flex-col items-start gap-4"> + <div> + <h2 class="p-name text-left text-2xl mb-2">{project.name}</h2> + <div class="mb-3 text-base font-normal"> + {#each tags as tag} + <span class="btn btn-sm btn-ghost normal-case border-dotted border-base-content/20 border-2 my-1 mr-1">{tag}</span> + {/each} + </div> + </div> + <p class="text-left text-base font-normal opacity-70">{@html project.description}</p> + </div> + </div> + </div> +</a> +{/if} +``` + +### 页面 + +- 参考:`/urara/friends/index.svelte` +- 新建:`/urara/projects/index.svelte` + +```html title="/urara/friends/index.svelte" +<script lang="ts"> + // @ts-nocheck + import Masonry from 'svelte-bricks' + import { Friend, friends as allFriends } from '$lib/config/friends' + import Head from '$lib/components/head.svelte' + import FriendComponent from '$lib/components/extra/friend.svelte' + + const rnd = Math.random() + const fy = (a: Friend[], r = 0, c = a.length) => { + while (c) (r = (rnd * c--) | 0), ([a[c], a[r]] = [a[r], a[c]]) + return a + } + let items: { id: string }[] = [...fy(allFriends as { id: string }[]), { id: 'footer' }] + let width: number, height: number +</script> + +<head /> + +<Masonry + {items} + minColWidth="{384}" + maxColWidth="{384}" + gap="{32}" + let:item + class="mx-4 sm:mx-8 md:my-4 lg:mx-16 lg:my-8 xl:mx-32 xl:my-16" + bind:width + bind:height> + <FriendComponent {item} /> +</Masonry> +``` + +**Projects 页面** + +因为我没有用到瀑布流布局,所以删掉了一些组件和 function + +```html title="/urara/projects/index.svelte" +<script lang="ts"> + import { projects as allProjects } from '$lib/config/projects' + import Head from '$lib/components/head.svelte' + import ProjectComponent from '$lib/components/extra/projects.svelte' + + let items: { id: string }[] = [...(allProjects as { id: string }[]), { id: 'footer' }] +</script> + +<head /> + +{#each items as item} +<ProjectComponent {item} /> +{/each} +``` + +### 响应式布局 + +参考 [Tailwind 的响应式设计指南](https://tailwindcss.com/docs/responsive-design) ,修改了卡片`flex` 的方向,以及图片的宽度,以适应小尺寸屏幕。 + +## 5. 测试 + +其实有错误的话 `pnpm dev` 以及 `pnpm build` 的时候都会提醒,但我后面发现也可以用 `pnpm check` 来检查。过程中我好像没有遇到什么 Bug。 + +## 6. Pull request 到 Github + +先看了一下 Repo 作者写的 [contributing docs](https://github.com/importantimport/urara/blob/main/.github/CONTRIBUTING.md),了解 Contribute 的规范。 + +按照相应步骤做了之后,Google 了一下如何 pull request,照着 FreeCodeCamp 的这篇进行了操作: +[如何在 GitHub 提交第一个 pull request](https://chinese.freecodecamp.org/news/how-to-make-your-first-pull-request-on-github/) ,然后成功 Pull 了一个 Request,但后面发现有的文件没有改,造成了 bug,就删除了原 Request 重新 Pull。 + +最后终于创建成功了我的第一个 Pull request! +链接:[feat: ✨ add project page by Sevichecc · Pull Request #19 · importantimport/urara · GitHub](https://github.com/importantimport/urara/pull/19) + +## 7. Last but not least + +写一篇这样的博文,并发表到互联网。 + +||好啦我知道这篇文章有点臭屁,但下次还敢……|| diff --git a/src/routes/2022-05-30-contabo-oss.md b/src/routes/2022-05-30-contabo-oss.md new file mode 100644 index 00000000..1c912b2b --- /dev/null +++ b/src/routes/2022-05-30-contabo-oss.md @@ -0,0 +1,88 @@ +--- +title: Contabo OSS + PicGo 图床配置 +created: 2022-05-30 +summary: OSS自建图床笔记 +tags: + - OSS +--- + +## 1. 购买套餐 + +在官网购买 OSS 套餐,按月付费:[Object Storage: S3-Compatible with Free Data Transfer](https://contabo.com/en/object-storage/) + +我没有修改设置,选的 250G 的容量,位于美国 + +## 2. 创建 Bucket + +进入控制面板:[Contabo Object Storage Panel](https://new.contabo.com/storage/object-storage/buckets),然后点击`Create Bucket` 创建存储桶 + + +其中 **Bucket Name** 可以随便写,**Select Region** 不用选,默认是购买 OSS 时所选择的区域,如果换区域也可以另选 + +创建好后如图: +(这个 Public Access 应该默认是红色的,也就是没有打开,我这里打开了) + + +## 3. 安装 PicGo 插件 + +在插件里面搜`s3`,然后安装第一个: + + +安装好后,在设置里选择打开, + + +## 4. 配置插件 + + + +### 应用密钥 ID 和 应用密钥 + +打开[Contabo Object Storage Panel](https://new.contabo.com/account/security)的 **Acount** > **Security & Access** 面板,找到最下面的 **S3 Object Storage Credentials** + +这里对应插件设置里的: + +- **应用密钥 ID** → **Access Key** +- **应用密钥** → **Secret Key** +  + +### 桶 / 自定义节点 /自定义域名 + +- **桶** → **Bucket Name** +- **自定义节点** → Bucket URL 中桶名字前的部分,比如这里就是`https://usc1.contabostorage.com` +  +- **自定义域名**,需要先点击 Quick Action 里面的 Share 标志,打开 Public Sharing,然后里面的链接对应的就是设置里的自定义域名: +  + +### 其他设置 + +打开这两项: + + +然后文件路径对应的是 Bucket 里面存储文件的路径,具体的设置可以参照:[GitHub - wayjam/picgo-plugin-s3: PicGo S3 插件](https://github.com/wayjam/picgo-plugin-s3) + +## 5. 其他 + +### Obsidian 中的图床设置 + +先安装这个`Image auto upload Plugin`插件: + + +然后在 PicGo 设置里面 → **设置 Server**→ 打开 Server 开关 + + + + +然后 Obsidian 插件中这样设置: + +PicGo Server :`http://127.0.0.1:36677/upload` + +端口号不一定是 36677,只要一一对应就好。 + + +设置好后,在后台保持 PicGo 开启,就可以在 Obsidian 里面粘贴一键上传图床了~ + +### 更多 PicGO 插件: + +[GitHub - PicGo/Awesome-PicGo: A collection of awesome projects using PicGo.](https://github.com/PicGo/Awesome-PicGo) + +有图床备份、图片压缩、图床转移等插件,不过我都没有试过…… diff --git a/src/routes/2022-06-10-backtotop.md b/src/routes/2022-06-10-backtotop.md new file mode 100644 index 00000000..c1ab7e97 --- /dev/null +++ b/src/routes/2022-06-10-backtotop.md @@ -0,0 +1,91 @@ +--- +title: 实现一个返回页面顶部的 Vue3 组件 +created: 2022-06-10 +summary: 结合流畅的动画平滑滚动到页面顶部 +tags: + - Vue3 + - BootStrap +--- + +主要参考:[Simple Vue.js and Tailwind.css Scroll To Top Button | Adam Bailey](https://adambailey.io/blog/scroll-to-top-button-vue/) + +CSS 库:[Bootstrap V5.2](https://getbootstrap.com/docs/5.2/getting-started/introduction/) + +- 按钮的布局方式为 sticky +- 因为可能需要频繁切换显示状态,所以用`v-show` 而不是 `v-if`来控制按钮可见性 +- 使用 Vue 中内置的`<transition>`组件实现状态之间的平滑过渡 + +```vue title="BackToTop.vue" +<template> + <div class="position-sticky bottom-0 end-0 w-100 d-flex justify-content-end b-0 pb-3 pe-5"> + <transition> + <button class="btn btn-secondary btn-sm" v-show="isVisible" @click="scrollToTop" aria-label="scroll to top of the page"> + <img src="../assets/to-top.min.svg" alt="an arrow point to top" /> + </button> + </transition> + </div> +</template> + +<script lang="ts"> +import { defineComponent, ref, onMounted, onBeforeUnmount } from 'vue' + +export default defineComponent({ + name: 'BackToTop', + setup() { + const isVisible = ref(false) + const handleScroll = () => { + isVisible.value = window.scrollY > 0 + } + const scrollToTop = () => { + window.scrollTo({ + top: 0, + behavior: 'smooth' + }) + } + onMounted(() => { + window.addEventListener('scroll', handleScroll) + }) + onBeforeUnmount(() => { + window.removeEventListener('scroll', handleScroll) + }) + return { + isVisible, + handleScroll, + scrollToTop + } + } +}) +</script> + +<style> +.v-enter-active, +.v-leave-active { + transition: opacity 0.2s ease; +} +.v-enter-from, +.v-leave-to { + opacity: 0; +} +</style> +``` + +```html title="to-top.min.svg" +<svg width="20" height="20" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path + d="M24.008 14.1V42M12 26l12-12 12 12M12 6h24" + stroke="#fff" + stroke-width="4" + stroke-linecap="round" + stroke-linejoin="round" /> +</svg> +``` + +其他参考/实现方式: + +- [vue 返回顶部的组件 BackTop](https://blog.csdn.net/m0_46217225/article/details/117933815) +- [vue-simple-scroll-up/ScrollToTop.vue · GitHub](https://github.com/asdf1899/vue-simple-scroll-up/blob/master/src/ScrollToTop.vue) +- [Vue.js - Scroll Back To Top Button Without Library](https://codepen.io/webty_mizusawa/pen/QWLMeqE) +- [Vue3 从 0 到 1 组件开发-基础组件:BackTop 回顶 - 掘金](https://juejin.cn/post/6993729338843594783) +- [vue 相同路径刷新怎么回到顶部 - 掘金](https://juejin.cn/post/6873264845915947016) + +题外话:||BootStrap 的文档写得好烂|| diff --git a/src/routes/2022-06-12-cloudflare.md b/src/routes/2022-06-12-cloudflare.md new file mode 100644 index 00000000..a8016385 --- /dev/null +++ b/src/routes/2022-06-12-cloudflare.md @@ -0,0 +1,129 @@ +--- +title: VPS · 配置 Cloudflare 的免费 SSL 证书 +created: 2022-06-12 +summary: 关于如何为多个域名配置SSL证书的操作笔记 +tags: + - Nginx + - VPS +--- + +声明:我不知道这样安不安全哈,It just works,个人笔记,操作有风险 + +参考:[申请 CloudFlare 免费 SSL 证书并应用到 nginx – 65536.io | 自娱自乐](https://65536.io/2020/03/607.html) + +先设置加密方式为`完全` ,否则之后可能会出现 526 错误 + + + +## 1. 创建证书 + +首先将主域名绑定到 Cloudflare,然后在`SSL/TLS` 下的源服务器证书处,点击`创建证书` + + +然后选择私钥和 CSR 生成方式,以及证书的有效期(也可以不改),点击`创建` + + +将下面的证书和私钥暂时复制到某个安全的地方,点击确定 + + +## 2. 密钥上传到 VPS + +连接 VPS 之后,创建一个文件夹存入密钥,我将其存到`/etc/nginx/cert/` 路径下 + +```bash +sudo mkdir /etc/nginx/cert && cd /etc/nginx/cert +``` + +写入证书,粘贴入刚刚保存的`证书` 栏里面的内容 + +```bash +sudo nano public.pem +``` + +修改权限 + +```bash +sudo chmod 644 public.pem +``` + +写入私钥,粘贴入刚刚保存的`私钥` 栏里面的内容 + +```bash +sudo nano private.key +``` + +修改权限 + +```bash +sudo chmod 600 private.key +``` + +## 3. 修改 Nginx 配置 + +参考:[How to Redirect HTTP to HTTPS in Nginx](https://phoenixnap.com/kb/redirect-http-to-https-nginx) + +如果有用防火墙,请先打开 80 端口和 443 端口,不然可能会像我一样,卡在一个毫无意义的 522 Error 上 ^ ^ + +```bash +sudo ufw allow 80 +sudo ufw allow 443 +``` + +打开 Nginx 配置 + +```bash +sudo nano /etc/nginx/nginx.conf +``` + +在 http 块里面配置一个默认 server,将 http 重定向到 https + +```bash + # 默认server + server { + listen 80 default_server; + server_name _; + return 301 https://$host$request_uri; + } + + # ssl配置 + server { + listen 443 ssl default_server; + server_name *.example1.com; + + ssl_certificate /etc/nginx/cert/public.pem; + ssl_certificate_key /etc/nginx/cert/private.key; + } + +# 如果有多个域名,可以这样配置,证书也要按之前的添加一下 + server { + listen 443 ssl; + server_name *.example2.com; + + ssl_certificate /etc/nginx/cert/example2/public.pem; + ssl_certificate_key /etc/nginx/cert/example2/private.key; + } +``` + +然后`sudo nginx -t` 测试一下,没有问题的话就可以 Nginx 了: + +```bash +sudo systemctl reload nginx +``` + +之后如果有域名要配置 ssl ,如 example.conf 中,可以直接将 `listen 80` 改为`listen 443 ssl` + +我常用的一个反代配置: + +```conf + server { + listen 443 ssl; + server_name 域名 + location / { + proxy_pass http://127.0.0.1:反代端口; + proxy_set_header HOST $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + } +``` diff --git a/src/routes/2022-07-13-sql.md b/src/routes/2022-07-13-sql.md new file mode 100644 index 00000000..1b89f1fd --- /dev/null +++ b/src/routes/2022-07-13-sql.md @@ -0,0 +1,134 @@ +--- +title: SQL 基础笔记 +created: 2022-07-13 +summary: 关于增改删查的方式 +tags: + - SQL +--- + +- 课程:[The Complete 2022 Web Development Bootcamp](https://www.udemy.com/share/1013gG3@wP9ybulEki65OWpaP1-gXEeRPJl4aj8eZNX7YYjFOgXlrxBGQyU6NniyJf2PqDI1EA==/) +- 工具:[SQL Online Compiler - for Data Science](https://sqliteonline.com) +- 教程:[SQL 教程 | 菜鸟教程](https://www.runoob.com/sql/sql-tutorial.html) + +## SQL vs. NOSQL + + + + + +- SQL + - 注重结构 +- NOSQL + - 更灵活,也更稳健 + - MoogoDB + +## CRUD + +### Create + +```sql +CREATE TABLE product( + id Int NOT NULL, + name STRING, + price MONEY, + PRIMARY KEY(id) + ) +``` + +- `NOT NULL` 当此值为 null 时,不创建列 +- `PRIMARY KEY(id)` 主键必须包含唯一的值,这个不能有重复的值 + +**插入数值** +第一种形式无需指定要插入数据的列名,只需提供被插入的值即可 + +```sql +INSERT INTO _table_name_ +VALUES (_value1_,_value2_,_value3_,...); +``` + +第二种形式需要指定列名及被插入的值: + +```sql +INSERT INTO _table_name_ (_column1_,_column2_,_column3_,...) +VALUES (_value1_,_value2_,_value3_,...); +``` + +### Read + +SELECT 语句用于从数据库中选取数据。 + +```sql +SELECT _column_name_,_column_name_ +FROM _table_name_; +``` + +```sql +SELECT * FROM _table_name_; +``` + +`*` 表示选择全部 + +可以用`WHERE` 筛选选择结果,如: + +```sql +SELECT _column_name_,_column_name_ +FROM _table_name_ +WHERE _column_name operator value_; +``` + +### Update + +```sql +UPDATE _tablse_name_ +SET _column1_=_value1_,_column2_=_value2_,... +WHERE _some_column_=_some_value_; +``` + +**ALTER TABLE 语句用于在已有的表中添加、删除或修改列。** +如需在表中添加列,请使用下面的语法: + +```sql +ALTER TABLE table_name +ADD column_name datatype +``` + +如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式): + +```sql +ALTER TABLE table_name +DROP COLUMN column_name +``` + +### Destory + +```sql +DELETE FROM _table_name_ +WHERE _some_column_=_some_value_; +``` + +## Understanding SQL Relationships, Foreign Keys and Inner Joins + +### FOREIGN KEY + +- 用`FOREIGN KEY` 来和外部表单链接 +- 一个表中的 FOREIGN KEY 指向另一个表中的 UNIQUE KEY(唯一约束的键)。 + +### INNER JOIN + +选择相应列并合并表,`ON`后面写的是条件 + +```sql +SELECT _column_name(s)_ +FROM _table1_ +INNER JOIN _table2_ +ON _table1.column_name_=_table2.column_name_; +``` + +或: + +```sql +SELECT _column_name(s)_ +FROM _table1_ +JOIN _table2_ +ON _table1.column_name_=_table2.column_name_; +``` diff --git a/src/routes/2022-07-23-d3.md b/src/routes/2022-07-23-d3.md new file mode 100644 index 00000000..82208b5a --- /dev/null +++ b/src/routes/2022-07-23-d3.md @@ -0,0 +1,446 @@ +--- +title: D3.js 基础笔记 +created: 2022-07-23 +summary: 即使是FreeCodeCamp也要做笔记 +tags: + - D3.js + - 数据可视化 +--- + +内容出自:[FreeCodeCamp](https://chinese.freecodecamp.org/learn/data-visualization) + +> D3 或 D3.js 表示数据驱动文档。它是一个用于在浏览器中创建动态和交互式数据视觉化的 JavaScript 库。[^1] + +## 学习资源 + +- [D3.js - Data-Driven Documents](https://d3js.org/) +- [Data Visualization with D3 – Full Course for Beginners [2022] - YouTube](https://www.youtube.com/watch?v=xkBheRZTkaw&t=1s) + - [Get it Right in Black & White Index - Get it Right in Black & White - VizHub Forum](https://vizhub.com/forum/t/get-it-right-in-black-white-index/110/2) + - [New Livestream Series: Get it Right in Black and White](https://vizhub.com/blog/2021/02/20/new-livestream-series-get-it-right-in-black-and-white/) + - [Get it Right in Black & White Index - Get it Right in Black & White - VizHub Forum](https://vizhub.com/forum/t/get-it-right-in-black-white-index/110) +- [The D3 Graph Gallery – Simple charts made with d3.js](https://d3-graph-gallery.com/) +- [Introduction to D3.js | D3 in Depth](https://www.d3indepth.com/introduction/) +- [数据可视化 认证 | freeCodeCamp.org](https://chinese.freecodecamp.org/learn/data-visualization/#data-visualization-with-d3) +- [Amelia Wattenberger](https://wattenberger.com/blog/d3) +- [GitHub - xswei/d3js_doc: D3js 中文文档 D3 中文](https://github.com/xswei/d3js_doc) + +## 基础操作 + +### 修改元素 + +- `select()` : + - 功能:从文档中选择一个元素。 + - 参数:它接受你想要选择的元素的名字作为参数,并返回文档中第一个与名字匹配的 HTML 节点。 +- `selectAll()` + - 选择一组元素。 并以 HTML 节点数组的形式返回该文本中所有匹配所输入字符串的对象 +- `append()` + - 功能:将 HTML 节点附加到选定项目,并返回该节点的句柄。 + - 参数:接收你希望添加到文档中的元素 +- `text()` + - 功能:设置所选节点的文本,也可以获取当前文本。 也可以用来添加标签 + - 参数:字符串或者回调函数 + +```js +const anchor = d3.select('a') +``` + +在 D3 中可以串联多个方法,连续执行一系列操作。->[[function chaining|链式调用]] + +### 使用数据 + +[d3-selection](https://github.com/xswei/d3-selection/blob/master/README.md#selection_data) + +- `data()`: + - 将元素与数据绑定 + - 不需要写 for 循环或者用 forEach() 迭代数据集中的对象。 data() 方法会解析数据集,任何链接在 data() 后面的方法都会为数据集中的每个对象运行一次。 + - 可以使用方括号的方式,如 `d[0]`,来访问数组中的值。 +- `enter()`:获取需要插入的选择集(数据个数大于元素个数)的占位符. + +> 当 enter() 和 data() 方法一起使用时,它把从页面中选择的元素和数据集中的元素作**比较**。 如果页面中选择的元素较少则创建缺少的元素。 + +可以理解为管理仓库的,选择的元素是货架,数据是货,如果货架不够了,就再补几个货架,如果多了就不管 + +```html +<body> + <ul></ul> + <script> + const dataset = ['a', 'b', 'c'] + d3.select('ul').selectAll('li').data(dataset).enter().append('li').text('New item') + </script> +</body> +``` + +### 使用动态数据 + +text 中设置回调处理数据如: + +- `d3.json()`: 从指定的 input URL 获取 JSON 文件。如果指定了 init 则会将其传递给底层的 fetch 方法 + +```html +<body> + <script> + const dataset = [12, 31, 22, 17, 25, 18, 29, 14, 9] + + d3.select('body') + .selectAll('h2') + .data(dataset) + .enter() + .append('h2') + .text(d => d + ' USD') + </script> +</body> +``` + +### 给元素添加内联样式 + +- `style()` + - 功能:在动态元素上添加内联 CSS 样式 + - 参数:以用逗号分隔的键值对作为参数 + +```js +selection.style('color', 'blue') + +//用回调过滤 +selection.style('color', d => { + return d < 20 ? 'red' : 'green' +}) +// 动态设置样式 +selection.style('height', d => d + 'px') // 动态设置高度 +``` + +### 添加 Class + +- `attr()` + - 功能:可以给元素添加任何 HTML 属性,包括 class 名称。 + - 参数: + - attr() 方法和 style() 的使用方法一样。 它使用逗号分隔值,并且可以使用回调函数 + - 可接收一个回调函数来动态设置属性。 这个回调函数有两个参数,一个是数据点本身(通常是 `d`),另一个是该数据点在数组中的下标 i, 这个参数是可选的 + - 当需要添加 class 时,class 参数保持不变,只有 container 参数会发生变化。 + +```js +selection.attr('class', 'container') +// 回调 +selection.attr('property', (d, i) => {}) + +// 比如改变间距 +svg + .selectAll('rect') + .data(dataset) + .enter() + .append('rect') + .attr('x', (d, i) => { + return i * 30 //改变间距 + }) + .attr('y', (d, i) => { + return d * 3 //改变高度 + }) + //悬停效果 + .attr('class', 'bar') +``` + +### 标签 + +- 和 rect 元素类似,text 元素也需要 x 和 y 属性来指定其放置在 SVG 画布上的位置, 它也需要能够获取数据来显示数据值。 +- 标签样式 + - `fill` 属性为 text 节点设置文本颜色 + - `style()` 方法设置其它样式的 CSS 规则,例如 font-family 或 font-size。 + +```js +//添加标签 +svg + .selectAll('text') + .data(dataset) + .enter() + .append('text') + .attr('x', (d, i) => i * 30) + .attr('y', (d, i) => h - 3 * d - 3) // 高三个单位是减 + .text(d => d) + // 添加样式 + .attr('fill', 'red') + .style('font-size', '25px') + //悬停效果 + .attr('class', 'bar') +/** css中 + .bar:hover { + fill: brown; + } + **/ +``` + +### 添加工具提示 tooltip + +- 当用户在一个对象上悬停时,提示框可以显示关于这个对象更多的信息 + +1. title + +```js +svg + .selectAll('rect') + .data(dataset) + .enter() + .append('rect') + .attr('x', (d, i) => i * 30) + .attr('y', (d, i) => h - 3 * d) + .attr('width', 25) + .attr('height', (d, i) => d * 3) + // 每个 rect 节点下附加 title 元素。 然后用回调函数调用 text() 方法使它的文本显示数据值。 + .append('title') + .text(d => d) +``` + +## SVG + +- 坐标系:坐标轴的原点在左上角。 x 坐标为 0 将图形放在 SVG 区域的左边缘, y 坐标为 0 将图形放在 SVG 区域的上边缘。 x 值增大矩形将向右移动, y 值增大矩形将向下移动。 + +### 创建 SVG + +```js +//创建svg +selection.append('svg') +``` + +### 反转 SVG 元素 + +- 为了使条形图向上,需要改变 y 坐标计算的方式 + +> SVG 区域的高度为 100。 如果在集合中一个数据点的值为 0,那么条形将从 SVG 区域的最底端开始(而不是顶端)。 为此,y 坐标的值应为 100。 如果数据点的值为 1,你将从 y 坐标为 100 开始来将这个条形设置在底端, 然后需要考虑该条形的高度为 1,所以最终的 y 坐标将是 99。 + +(高度从下面开始计算,坐标轴从上面开始) + +- y 坐标为 `y = heightOfSVG - heightOfBar` 会将条形图向上放置。 +- 通常,关系是 `y = h - m * d`,其中 m 是缩放数据点的常数。 + +### 更改 SVG 元素的颜色 + +- 在 SVG 中, rect 图形用 `fill` 属性着色。 它支持十六进制代码、颜色名称、rgb 值以及更复杂的选项,比如渐变和透明。 + +```js +svg + .selectAll('rect') + .data(dataset) + .enter() + .append('rect') + .attr('x', (d, i) => i * 30) + .attr('y', (d, i) => h - 3 * d) + .attr('width', 25) + .attr('height', (d, i) => 3 * d) + //将所有条形图的 fill 设置为海军蓝。 + .attr('fill', 'navy') +``` + +[^1]: [数据可视化 认证 | freeCodeCamp.org](https://chinese.freecodecamp.org/learn/data-visualization/) + +### SVG 图形 + +- SVG 支持多种图形,比如矩形和圆形, 并用它们来显示数据。 + +#### 矩形 + +SVG 的 rect 有四个属性。 x 和 y 坐标指定图形放在 svg 区域的位置, height 和 width 指定图形大小 + +```js +const svg = d3 + .select('body') + .append('svg') + .attr('width', w) + .attr('height', h) + .append('rect') //rect矩形 + .attr('width', 25) + .attr('height', 100) + .attr('x', 0) + .attr('y', 0) +``` + +#### 圆形 + +- SVG 用 circle 标签来创建圆形 +- circle 有三个主要的属性。 + - cx 和 cy 属性是坐标。 它们告诉 D3 将图形的中心放在 SVG 画布的何处。 + - 半径( r 属性)给出 circle 的大小。 +- 和 rect 的 y 坐标一样,circle 的 cy 属性是从 SVG 画布的顶端开始测量的,而不是从底端。 + +```js +svg + .selectAll('circle') + .data(dataset) + .enter() + .append('circle') + //散点图 + .attr('cx', d => d[0]) + .attr('cy', d => h - d[1]) + .attr('r', '5') +``` + +## 比例尺 + +### 创建线性比例 + +> 条形图和散点图都直接在 SVG 画布上绘制数据。 但是,如果一组的高或者其中一个数据点比 SVG 的高或宽更大,它将跑到 SVG 区域外。 + +> D3 中,比例尺可帮助布局数据。 scales 是函数,它告诉程序如何将一组原始数据点映射到 SVG 画布上。 + +#### 线性缩放 + +- 通常使用于定量数据 +- 默认情况下,比例尺使用一对一关系(identity relationship)。 输入的值和输出的值相同。 + +```js +const scale = d3.scaleLinear() +``` + +例子 + +```js +const scale = d3.scaleLinear() // 在这里创建轴 +const output = scale(50) // 调用 scale,传入一个参数 +d3.select('body').append('h2').text(output) +``` + +#### 按比例设置域和范围 + +- 域 domain:假设有一个数据集范围为 50 到 480, 这是缩放的**输入信息**,也被称为域。 +- 范围 range:沿着 x 轴将这些点映射到 SVG 画布上,位置介于 10 个单位到 500 个单位之间。 这是**输出信息**,也被称为范围。 +- `domain()` 和 `range()` 方法设置比例尺的值, 它们都接受一个至少有两个元素的数组作为参数。 + - `domain()` 方法给比例尺传递关于散点图原数据值的信息 + - `range()` 方法给出在页面上进行可视化的实际空间信息 + +例子: + +```js +scale.domain([50, 480]); //域 +scale.range([10, 500]); //范围 +scale(50) //10 +scale(480) //500 +scale(325) //323.37 +scale(750)。// 807.。67 +d3.scaleLinear() +``` + +按顺序,将在控制台中显示以下值:10、500、323.37 和 807.67。 + +注意,比例尺使用了域和范围之间的线性关系来找出给定数字的输出值。 域中的最小值(50)映射为范围中的最小值(10)。 + +(也就是给定范围,用线性关系缩小,比如图片放大缩小,给了原图大小和缩小后的图片大小,根据线性关系比例来计算每个像素的位置,元素的大小) + +#### 最小值最大值 + +- `d3.min`:最小值 +- `d3.max`: 最大值 +- `min()` 和 `max()` 都可以使用回调函数,下面例子中回调函数的参数 d 是当前的内部数组。 +- `min()` 和 `max()` 方法在设置比例尺时十分有用 + +例子:找到二维数组的最大值和最小值 + +```js +const locationData = [ + [1, 7], + [6, 3], + [8, 3] +] +const minX = d3.min(locationData, d => d[0]) //查找在d[0]位置上最小的值 +``` + +### 使用动态比例 + +- 用`min()` 和 `max()` 来确定比例尺范围和域 +- `padding` 将在散点图和 SVG 画布边缘之间添加空隙。 +- 保持绘图在右上角。 当为 y 坐标设置 range 时,大的值(height 减去 padding)是第一个参数,小的值是第二个参数。 + +```js +const dataset = [ + [34, 78], + [109, 280], + [310, 120], + [79, 411], + [420, 220], + [233, 145], + [333, 96], + [222, 333], + [78, 320], + [21, 123] +] +const w = 500 +const h = 500 + +const padding = 30 +const xScale = d3 + .scaleLinear() + .domain([0, d3.max(dataset, d => d[0])]) + .range([padding, w - padding]) +``` + +### 使用预定义的比例放置元素 + +- 用比例尺函数为 SVG 图形设置坐标属性值。 +- 当显示实际数据值时,不用使用比例尺,例如,在提示框或标签中的 `text()` 方法。 + +```js +svg + .selectAll('circle') + .data(dataset) + .enter() + .append('circle') + .attr('cx', d => xScale(d[0])) + .attr('cy', d => yScale(d[1])) + .attr('r', '5') +``` + +### 添加坐标轴 + +- 位置:`axisLeft()` 和 `axisBottom()`。 +- g 元素, g 是英文中组(group)的缩写,在渲染时,轴只是一条直线。 因为它是一个简单的图形,所以可以用 g +- SVG 支持多种 transforms,但是定位轴需要使用 translate 属性 + +例子: + +```js +// X轴 +const xAxis = d3.axisBottom(xScale) + +svg + .append('g') + .attr('transform', 'translate(0, ' + (h - padding) + ')') // translate(0,x) + .call(xAxis) // x轴作为参数被传递给 call() 方法 + +// Y轴 +const yAxis = d3.axisLeft(yScale) + +svg + .append('g') + .attr('transform', 'translate(0,' + (h - padding) + ')') + .call(xAxis) +svg + .append('g') + .attr('transform', 'translate(' + padding + ',0)') + .call(yAxis) +``` + +## 常见图表 + +### 散点图 + +> 圆圈来映射数据点,每个数据点有两个值。 这两个值和 x 和 y 轴相关联,在可视化中用来给圆圈定位。 + +```js +svg + .selectAll('circle') + .data(dataset) + .enter() + .append('circle') + .attr('cx', d => d[0]) + //反转图像 + .attr('cy', d => h - d[1]) + .attr('r', '5') + +// 散点图加标签 +svg + .selectAll('text') + .data(dataset) + .enter() + .append('text') + // Add your code below this line + + .attr('x', d => d[0] + 5) + .attr('y', d => h - d[1]) + .text(d => d[0] + ', ' + d[1]) +``` diff --git a/src/routes/2022-07-27-bin.md b/src/routes/2022-07-27-bin.md new file mode 100644 index 00000000..11c43413 --- /dev/null +++ b/src/routes/2022-07-27-bin.md @@ -0,0 +1,31 @@ +--- +title: JS中的二进制数字 +created: 2022-07-27 +summary: 0b/0B和paresInt +tags: + - JavaScript +--- + +参考:[How to Represent Binary Numbers in JavaScript? - Designcise](https://www.designcise.com/web/tutorial/how-to-represent-binary-numbers-in-javascript) + +## ES6+ + +在 ES6 之后的版本,在二进制数字前加`0b` 或者`0B`来标识这是一个二进制数字,比如: + +```js +let number5 = ob101 +let number5 = oB101 +``` + +## Before ES6 + +- 通过字符串和 parseInt 来转换 +- parseInt 可以在字符串中提取数字,第一个参数是要提取的字符串,第二个是基准的计算进制 + +```js +const number = '0101' + +Number.parseInt(number, 2) +``` + +相关:[JavaScript · 十进制数转二进制](/2022-03-04-decbin) diff --git a/src/routes/2022-08-12-vue-challenges.md b/src/routes/2022-08-12-vue-challenges.md new file mode 100644 index 00000000..0c05db8e --- /dev/null +++ b/src/routes/2022-08-12-vue-challenges.md @@ -0,0 +1,1237 @@ +--- +title: Vue.js 挑战练习 +created: 2022-08-12 +summary: 我的答案以及相关知识点 +tags: + - Vue +--- + +最近做了一下这个[Vue.js 挑战](https://cn-vuejs-challenges.netlify.app/questions/14-dynamic-css-values/README.zh-CN.html),其中的题目大多出自[Vue3 文档](https://staging-cn.vuejs.org/),都不是很难,但涉及到的知识点 +比较琐碎,用来复习挺好的。 + +然后这是我的答案和题目涉及到的知识点,除了[鼠标指针](###鼠标指针)这个部分没通过单元测试之外,其他都都通过了,然后这个鼠标指针为什么没通过单元测试我也没弄明白,试了下其他人的也通过不了,好奇怪…… + +这里省去部分题目,主要写答案。 + +## Built-ins + +### DOM 传送门 + +Vue.js 提供了一个内置组件,将其插槽内容渲染到另一个 DOM,成为该 DOM 的一部分。 + +```vue +<script setup> +const msg = 'Hello World' +</script> +<template> + <teleport to="body"> + <span>{{ msg }}</span> + </teleport> +</template> +``` + +相关知识点 :[Teleport | Vue.js](https://v3.cn.vuejs.org/guide/teleport.html#teleport) + +> 有时组件模板的一部分逻辑上属于该组件,而从技术角度来看,最好将模板的这一部分移动到 DOM 中 Vue app 之外的其他位置[^1]。 + +- 有点像传送门,将相应元素渲染到制定位置 +- to 后面写 css selector + +### 优化性能的指令 + +Vue.js 提供了一个指令,以便只渲染一次元素和组件,并且跳过以后的更新。 + +```vue +<script setup> +import { ref } from 'vue' + +const count = ref(0) + +setInterval(() => { + count.value++ +}, 1000) +</script> + +<template> + <span v-once>使它从不更新: {{ count }}</span> +</template> +``` + +相关知识点:`Vue-事件修饰符` + +## CSS Features + +### 动态 CSS + +Vue 单文件组件 `<style>` 模块支持给 CSS 绑定动态值。 + +```vue +<script setup> +import { ref } from 'vue' +const theme = ref('red') + +const colors = ['blue', 'yellow', 'red', 'green'] + +setInterval(() => { + theme.value = colors[Math.floor(Math.random() * 4)] +}, 1000) +</script> + +<template> + <p>hello</p> +</template> + +<style scoped> +/* Modify the code to bind the dynamic color */ +p { + color: v-bind(theme); +} +</style> +``` + +相关知识点:`v-bind` `Dynamic Styling动态绑定样式` + +### 全局 CSS + +给具有 CSS 作用域的 Vue 单文件组件设置全局 CSS 样式 + +```vue +<template> + <p>Hello Vue.js</p> +</template> + +<style scoped> +p { + font-size: 20px; + color: red; + text-align: center; + line-height: 50px; +} + +/* Make it work */ +:global(body) { + width: 100vw; + height: 100vh; + background-color: burlywood; +} +</style> +``` + +或者 + +```vue +<template> + <p>Hello Vue.js</p> +</template> + +<style scoped> +p { + font-size: 20px; + color: red; + text-align: center; + line-height: 50px; +} +</style> +<style> +/* Make it work */ +body { + width: 100vw; + height: 100vh; + background-color: burlywood; +} +</style> +``` + +相关知识点:[单文件组件 CSS 功能 | Vue.js](https://staging-cn.vuejs.org/api/sfc-css-features.html) + +## Components + +### DOM 传送门 + +见上面 + +### Props 验证 + +验证 Button 组件的 Prop 类型 ,使它只接收: primary | ghost | dashed | link | text | default ,且默认值为 default + +```vue +<script setup> +import Button from './Button.vue' +defineProps({ + type: { + type: String, + default: 'default', + validator: value => { + ;['primary', 'ghost', 'dashed', 'link', 'text', 'default'].includes(value) + } + } +}) +</script> + +<template> + <Button type="dashed" /> +</template> +``` + +相关知识点:[Props | Vue.js](https://staging-cn.vuejs.org/guide/components/props.html#prop-validation) + +### 函数式组件 + +这题我不是很懂,翻了一下大家的解决方案,感觉这个比较能看懂:[21 - functional component · Issue #322 · webfansplz/vuejs-challenges · GitHub](https://github.com/webfansplz/vuejs-challenges/issues/322) + +```vue +<script setup lang="ts"> +import { ref, h } from 'vue' + +/** + * Implement a functional component : + * 1. Render the list elements (ul/li) with the list data + * 2. Change the list item text color to red when clicked. + */ +const ListComponent = (props, { emit }) => + h( + // 创建 ul + 'ul', + // 根据传入的props创建li + props.list.map((item: { name: string }, index: number) => + h( + 'li', + { + // 点击时处罚toggle。并将当前index作为参数传入toggle + onClick: () => emit('toggle', index), + // 将当前点击的li颜色设置为红色 + style: index === props.activeIndex ? { color: 'red' } : null + }, + // li 默认值 + item.name + ) + ) + ) +ListComponent.props = ['list', 'activeIndex'] +ListComponent.emits = ['toggle'] + +const list = [ + { + name: 'John' + }, + { + name: 'Doe' + }, + { + name: 'Smith' + } +] + +const activeIndex = ref(0) + +function toggle(index: number) { + activeIndex.value = index +} +</script> + +<template> + <list-component :list="list" :active-index="activeIndex" @toggle="toggle" /> +</template> +``` + +相关知识点: + +- [渲染函数 & JSX | Vue.js](https://staging-cn.vuejs.org/guide/extras/render-function.html#functional-components) +- [渲染机制 | Vue.js](https://staging-cn.vuejs.org/guide/extras/rendering-mechanism.html) + +### 渲染函数[h()] + +> 使用 h 渲染函数来实现一个组件。 + +```vue +import { defineComponent, h } from 'vue'; export default defineComponent({ name: 'MyButton', props: { disabled: { type: Boolean, +default: false, }, }, emits: ['custom-click'], setup(props, { emit, slots }) { return () => h( 'button', { disabled: +props.disabled, onClick: () => emit('custom-click'), }, slots.default?.() ); }, }); +``` + +### 树组件 + +实现一个树组件 + +```vue +<script setup lang="ts"> +import { defineComponent } from 'vue' +interface TreeData { + key: string + title: string + children: TreeData[] +} +defineProps<{ data: TreeData[] }>() +</script> + +<template> + <ul v-for="node in data"> + <li>{{ node.title }}</li> + <template v-if="node.children && node.children.length"> + // 用递归的方法来实现 + <TreeComponent :data="node.children" /> + </template> + </ul> +</template> +``` + +参考: + +- [208 - Tree Component · Issue #659 · webfansplz/vuejs-challenges · GitHub](https://github.com/webfansplz/vuejs-challenges/issues/659) +- [Creating a Recursive Tree Component in Vue.js | DigitalOcean](https://www.digitalocean.com/community/tutorials/vuejs-recursive-components) + 相关知识点:[单文件组件 `<script setup>` | Vue.js](https://staging-cn.vuejs.org/api/sfc-script-setup.html#recursive-components) + +## Composable Function + +本节相关知识点:[组合式函数 | Vue.js](https://staging-cn.vuejs.org/guide/reusability/composables.html) + +### 切换器 + +尝试编写可组合函数 + +```vue +<script setup lang="ts"> +import { ref } from 'vue' +/** + * Implement a composable function that toggles the state + * Make the function work correctly + */ +function useToggle(init: boolean) { + const state = ref(init) + const toggle = () => (state.value = !state.value) + return [state, toggle] +} + +const [state, toggle] = useToggle(false) +</script> + +<template> + <p>State: {{ state ? 'ON' : 'OFF' }}</p> + <p @click="toggle">Toggle state</p> +</template> +``` + +### 计数器 + +实现一个计数器 + +```vue +<script setup lang="ts"> +import { ref } from 'vue' + +interface UseCounterOptions { + min?: number + max?: number +} + +/** + * Implement the composable function + * Make sure the function works correctly + */ +function useCounter(initialValue = 0, options: UseCounterOptions = {}) { + const count = ref(initialValue) + + const inc = () => { + if (count.value < options.max) count.value++ + } + + const dec = () => { + if (count.value > options.min) count.value-- + } + + const reset = () => (count.value = initialValue) + return { count, inc, dec, reset } +} + +const { count, inc, dec, reset } = useCounter(0, { min: 0, max: 10 }) +</script> +``` + +### 实现本地存储函数 + +封装一个`localStorage`API + +```vue +<script setup lang="ts"> +import { ref, watchEffect } from 'vue' + +/** + * Implement the composable function + * Make sure the function works correctly + */ +function useLocalStorage(key: string, initialValue: any) { + const value = ref(localStorage.getItem(key) || initialValue) + watchEffect(() => { + localStorage.setItem(key, value.value) + }) + return value +} + +const counter = useLocalStorage('counter', 0) + +// We can get localStorage by triggering the getter: +console.log(counter.value) + +// And we can also set localStorage by triggering the setter: + +const update = () => counter.value++ +</script> + +<template> + <p>Counter: {{ counter }}</p> + <button @click="update">Update</button> +</template> +``` + +相关知识点: + +- [watchEffect()](https://staging-cn.vuejs.org/api/reactivity-core.html#watcheffect) +- [Window.localStorage - Web API 接口参考 | MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/localStorage) + +### 鼠标坐标 + +这个没通过单元测试,不知道什么原因,试了下其他人的也没能通过…… + +```vue +<script setup lang="ts"> +import { ref, onMounted, onUnmounted } from 'vue' +// Implement ... +function useEventListener(target, event, callback) { + onMounted(() => target.addEventListener(event, callback)) + onUnmounted(() => target.removeEventListener(event, callback)) +} + +// Implement ... +function useMouse() { + const x = ref(0) + const y = ref(0) + useEventListener(window, 'mousemove', e => { + x.value = e.pageX + y.value = e.pageY + }) + return { x, y } +} +const { x, y } = useMouse() +</script> + +<template>Mouse position is at: {{ x }}, {{ y }}</template> +``` + +## Composition API + +### 生命周期钩子 + +```vue +<script setup lang="ts"> +import { onMounted, inject, onUnmounted } from 'vue' + +const timer = inject('timer') +const count = inject('count') + +onMounted(() => { + timer.value = window.setInterval(() => { + count.value++ + }, 1000) +}) +// 计时器要清除 +onUnmounted(() => { + window.clearInterval(timer.value) +}) +</script> + +<template> + <div> + <p>Child Component: {{ count }}</p> + </div> +</template> +``` + +- 相关知识点: + - [组合式 API:依赖注入 | Vue.js](https://staging-cn.vuejs.org/api/composition-api-dependency-injection.html) + - [组合式 API:生命周期钩子 | Vue.js](https://staging-cn.vuejs.org/api/composition-api-lifecycle.html#onunmounted) + +### ref 全家桶 + +```vue +<script setup lang="ts"> +import { ref, Ref, reactive, isRef, unref, toRef } from 'vue' + +const initial = ref(10) +const count = ref(0) + +// Challenge 1: Update ref +function update(value) { + count.value = value +} + +/** + * Challenge 2: Check if the `count` is a ref object. + * Make the output be 1 + */ +console.log(isRef(count) ? 1 : 0) + +/** + * Challenge 3: Unwrap ref + * Make the output be true + */ +function initialCount(value: number | Ref<number>) { + // Make the output be true + console.log(unref(value) === 10) +} + +initialCount(initial) + +/** + * Challenge 4: + * create a ref for a property on a source reactive object. + * The created ref is synced with its source property: + * mutating the source property will update the ref, and vice-versa. + * Make the output be true + */ +const state = reactive({ + foo: 1, + bar: 2 +}) +const fooRef = toRef(state, 'foo') // change the impl... + +// mutating the ref updates the original +fooRef.value++ +console.log(state.foo === 2) + +// mutating the original also updates the ref +state.foo++ +console.log(fooRef.value === 3) +</script> + +<template> + <div> + <p> + <span @click="update(count - 1)">-</span> + {{ count }} + <span @click="update(count + 1)">+</span> + </p> + </div> +</template> +``` + +相关知识点: + +- [isRef()](https://staging-cn.vuejs.org/api/reactivity-utilities.html#isref) +- [unref()](https://staging-cn.vuejs.org/api/reactivity-utilities.html#unref) +- [toRef](https://staging-cn.vuejs.org/api/reactivity-utilities.html#toref) + +### 响应性丢失 + +保证解构/扩展不丢失响应性 + +```vue +<script setup lang="ts"> +import { reactive, toRefs } from 'vue' + +function useCount() { + const state = reactive({ + count: 0 + }) + + function update(value: number) { + state.count = value + } + + return { + state: toRefs(state), + update + } +} + +// Ensure the destructured properties don't lose their reactivity +const { + state: { count }, + update +} = useCount() +</script> + +<template> + <div> + <p> + <span @click="update(count - 1)">-</span> + {{ count }} + <span @click="update(count + 1)">+</span> + </p> + </div> +</template> +``` + +相关知识点:[toRefs](https://staging-cn.vuejs.org/api/reactivity-utilities.html#torefs) + +### 可写的计算属性 + +```vue +<script setup lang="ts"> +import { ref, computed } from 'vue' + +const count = ref(1) +const plusOne = computed({ + get() { + return count.value + 1 + }, + set(newValue) { + count.value = newValue - 1 + } +}) + +/** + * Make the `plusOne` writable. + * So that we can get the result `plusOne` to be 3, and `count` to be 2. + */ + +plusOne.value++ +</script> + +<template> + <div> + <p>{{ count }}</p> + <p>{{ plusOne }}</p> + </div> +</template> +``` + +相关知识点:[可写的计算属性 ](https://staging-cn.vuejs.org/guide/essentials/computed.html#writable-computed) + +### watch 全家桶 + +```vue +<script setup lang="ts"> +import { ref, watch } from 'vue' + +const count = ref(0) + +/** + * Challenge 1: Watch once + * Make sure the watch callback only triggers once + */ +const watchOnce = watch(count, () => { + console.log('Only triggered once') + watchOnce() +}) + +count.value = 1 +setTimeout(() => (count.value = 2)) + +/** + * Challenge 2: Watch object + * Make sure the watch callback is triggered + */ +const state = ref({ + count: 0 +}) + +watch( + state, + () => { + console.log('The state.count updated') + }, + { deep: true } +) + +state.value.count = 2 + +/** + * Challenge 3: Callback Flush Timing + * Make sure visited the updated eleRef + */ + +const eleRef = ref() +const age = ref(2) +watch( + age, + () => { + console.log(eleRef.value) + }, + { + flush: 'post' + } +) +age.value = 18 +</script> + +<template> + <div> + <p> + {{ count }} + </p> + <p ref="eleRef"> + {{ age }} + </p> + </div> +</template> +``` + +相关知识点:[侦听器 | Vue.js](https://staging-cn.vuejs.org/guide/essentials/watchers.html) + +### 浅层 ref + +响应式 API: shallowRef + +```vue +<script setup lang="ts"> +import { shallowRef, watch } from 'vue' + +const state = shallowRef({ count: 1 }) + +// Does NOT trigger +watch( + state, + () => { + console.log('State.count Updated') + }, + { deep: true } +) + +/** + * Modify the code so that we can make the watch callback trigger. + */ +state.value = { count: 2 } +</script> + +<template> + <div> + <p> + {{ state.count }} + </p> + </div> +</template> +``` + +相关知识点:[shallowRef()](https://staging-cn.vuejs.org/api/reactivity-advanced.html#shallowref) + +### 依赖注入 + +child.vue + +```vue +<script setup lang="ts"> +import { inject } from 'vue' +const count = inject('count') +</script> + +<template> + {{ count }} +</template> +``` + +相关知识点:[组合式 API:依赖注入 | Vue.js](https://staging-cn.vuejs.org/api/composition-api-dependency-injection.html) + +### Effect 作用域 API + +```vue +<script setup lang="ts"> +import { ref, computed, watch, watchEffect, effectScope } from 'vue' + +const counter = ref(1) +const doubled = computed(() => counter.value * 2) + +// use the `effectScope` API to make these effects stop together after being triggered once + +const scope = effectScope() +scope.run(() => { + watch(doubled, () => console.log(doubled.value)) + watchEffect(() => console.log(`Count: ${doubled.value}`)) + counter.value = 2 +}) + +setTimeout(() => { + counter.value = 4 + scope.stop() +}) +</script> + +<template> + <div> + <p> + {{ doubled }} + </p> + </div> +</template> +``` + +相关知识点:[effectScope](https://staging-cn.vuejs.org/api/reactivity-advanced.html#effectscope) + +### 自定义 ref + +```vue +<script setup> +import { watch, customRef } from 'vue' + +/** + * Implement the function + */ +function useDebouncedRef(value, delay = 200) { + let timeout + return customRef((track, trigger) => { + return { + get() { + track() + return value + }, + set(newValue) { + clearTimeout(timeout) + timeout = setTimeout(() => { + value = newValue + trigger() + }, delay) + } + } + }) +} +const text = useDebouncedRef('hello') + +/** + * Make sure the callback only gets triggered once when entered multiple times in a certain timeout + */ +watch(text, value => { + console.log(value) +}) +</script> + +<template> + <input v-model="text" /> +</template> +``` + +相关知识点:[customRef](https://staging-cn.vuejs.org/api/reactivity-advanced.html#customref) + +## Directives + +### 大写 + +创建一个自定义的修饰符 `capitalize`,它会自动将 `v-model` 绑定输入的字符串值首字母转为大写: +App.vue + +```vue +<script setup> +import { ref } from 'vue' +import Input from './Input.vue' + +const value = ref('') +</script> + +<template> + <Input type="text" v-model.capitalize="value" /> +</template> +``` + +Input.vue + +```vue +<script setup> +import { defineProps, defineEmits } from 'vue' +const props = defineProps({ + modelValue: String, + modelModifiers: { + default: () => ({}) + } +}) + +const emit = defineEmits(['update:modelValue']) +function emitValue(e) { + let value = e.target.value + if (props.modelModifiers.capitalize) { + value = value.charAt(0).toUpperCase() + value.slice(1) + } + emit('update:modelValue', value) +} +</script> + +<template> + <input type="text" :value="modelValue" @input="emitValue" /> +</template> +``` + +相关知识点:[处理 v-model 修饰符](https://staging-cn.vuejs.org/guide/components/events.html#usage-with-v-model) + +### 优化性能的指令 + +见上面。v-once + +### 切换焦点指令 + +```vue +<script setup lang="ts"> +import { ref } from 'vue' + +const state = ref(false) + +/** + * Implement the custom directive + * Make sure the input element focuses/blurs when the 'state' is toggled + * + */ + +const VFocus = { + updated: (el, state) => (state.value ? el.focus() : el.blur()) +} + +setInterval(() => { + state.value = !state.value +}, 2000) +</script> + +<template> + <input v-focus="state" type="text" /> +</template> +``` + +相关知识点:[自定义指令 | Vue.js](https://staging-cn.vuejs.org/guide/reusability/custom-directives.html) + +### 防抖点击指令 + +尝试实现一个防抖点击指令 + +```vue +<script setup lang="ts"> +/** + * Implement the custom directive + * Make sure the `onClick` method only gets triggered once when clicked many times quickly + * And you also need to support the debounce delay time option. e.g `v-debounce-click:ms` + * + */ + +function debounce(fn, delay) { + let timeout + let count = 0 + return (...args) => { + if (count === 0) { + count++ + fn(...args) + } + clearTimeout(timeout) + timeout = setTimeout(() => { + fn(...args) + }, delay) + } +} + +const VDebounceClick = { + mounted: (el, binding) => { + const { value, arg } = binding + el.addEventListener('click', debounce(value, arg)) + } +} + +function onClick() { + console.log('Only triggered once when clicked many times quickly') +} +</script> + +<template> + <button v-debounce-click:200="onClick">Click on it many times quickly</button> +</template> +``` + +相关知识点:[指令钩子](https://staging-cn.vuejs.org/guide/reusability/custom-directives.html#introduce) + +### 激活的样式-指令 + +```vue +<script setup lang="ts"> +import { ref, watchEffect } from 'vue' + +/** + * Implement the custom directive + * Make sure the list item text color changes to red when the `toggleTab` is toggled + * + */ +const VActiveStyle = { + mounted: (el, binding) => { + const [styles, fn] = binding.value + watchEffect(() => { + Object.keys(styles).map(key => (el.style[key] = fn() ? styles[key] : '')) + }) + } +} + +const list = [1, 2, 3, 4, 5, 6, 7, 8] +const activeTab = ref(0) +function toggleTab(index: number) { + activeTab.value = index +} +</script> + +<template> + <ul> + <li + v-for="(item, index) in list" + :key="index" + v-active-style="[{ color: 'red' }, () => activeTab === index]" + @click="toggleTab(index)"> + {{ item }} + </li> + </ul> +</template> +``` + +### 实现简易版`v-model`指令 + +```vue +<script setup lang="ts"> +import { ref } from 'vue' + +/** + * Implement a custom directive + * Create a two-way binding on a form input element + * + */ +const VOhModel = { + mounted: (el, binding) => { + el.value = binding.value + el.addEventListener('input', () => { + value.value = el.value + }) + } +} + +const value = ref('Hello Vue.js') +</script> + +<template> + <input v-oh-model="value" type="text" /> + <p>{{ value }}</p> +</template> +``` + +## Event Handling + +### 阻止事件冒泡 + +```vue +<script setup lang="ts"> +const click1 = () => { + console.log('click1') +} + +const click2 = e => { + console.log('click2') +} +</script> + +<template> + <div @click="click1()"> + <div @click.stop="click2()">click me</div> + </div> +</template> +``` + +相关知识点:[事件修饰符](https://staging-cn.vuejs.org/guide/essentials/event-handling.html#event-modifiers) + +### 按键修饰符 + +```vue +<template> + <!-- Add key modifiers made this will fire even if Alt or Shift is also pressed --> + <button @click.alt="onClick1" @click.shift="onClick1">A</button> + + <!-- Add key modifiers made this will only fire when Shift and no other keys are pressed --> + <button @click.shift.exact="onCtrlClick">A</button> + + <!-- Add key modifiers made this will only fire when no system modifiers are pressed --> + <button @click.exact="onClick2">A</button> +</template> + +<script setup> +function onClick1() { + console.log('onClick1') +} +function onCtrlClick() { + console.log('onCtrlClick') +} +function onClick2() { + console.log('onClick2') +} +</script> +``` + +相关知识点:[按键修饰符](https://staging-cn.vuejs.org/guide/essentials/event-handling.html#key-modifiers) + +## Global API:General + +### 下一次 DOM 更新 + +在`Vue.js`中改变响应式状态时,DOM 不会同步更新。 `Vue.js` 提供了一个用于等待下一次 DOM 更新的方法 + +```vue +<script setup> +import { ref, nextTick } from 'vue' + +const count = ref(0) +const counter = ref(null) + +async function increment() { + count.value++ + + /** + * DOM is not yet updated, how can we make sure that the DOM gets updated + * Make the output be true + */ + await nextTick() + console.log(+counter.value.textContent === 1) +} +</script> + +<template> + <button ref="counter" @click="increment"> + {{ count }} + </button> +</template> +``` + +相关知识点:[nextTick()](https://staging-cn.vuejs.org/api/general.html#nexttick) + +## Lifecycle + +### 生命周期钩子 + +[同上:生命周期钩子](#生命周期钩子) + +## Reactivity:Advanced + +### 浅层 ref + +[同上:浅层 ref](#浅层-ref) + +### 原始值 API + +```vue +<script setup lang="ts"> +import { reactive, isReactive, toRaw, markRaw } from 'vue' + +const state = { count: 1 } +const reactiveState = toRaw(reactive(state)) + +/** + * Modify the code so that we can make the output be true. + */ +console.log(reactiveState === state) + +/** + * Modify the code so that we can make the output be false. + */ +const info = markRaw({ count: 1 }) +const reactiveInfo = reactive(info) + +console.log(isReactive(reactiveInfo)) +</script> + +<template> + <div> + <p> + {{ reactiveState.count }} + </p> + </div> +</template> +``` + +相关知识点: + +- [toRaw](https://staging-cn.vuejs.org/api/reactivity-advanced.html#toraw) +- [markRaw](https://staging-cn.vuejs.org/api/reactivity-advanced.html#markraw) + +### Effect 作用域 API + +[同上:Effect 作用域 API](#effect-作用域-api) + +### 自定义 ref + +[同上:自定义 ref](#自定义-ref) + +## Reactivity:Core + +### ref 全家桶 + +[同上:ref 全家桶](#ref-全家桶) + +### 可写的计算属性 + +[同上:可写的计算属性](#可写的计算属性) + +### watch 全家桶 + +[同上:watch 全家桶](#watch-全家桶) + +## Reactivity:Utilities + +### 响应性丟失 + +[同上:响应性丟失](#响应性丟失) + +## Utility Function + +### until + +```vue +<script setup lang="ts"> +import { ref } from 'vue' + +const count = ref(0) + +/** + * Implement the until function + */ + +function until(initial) { + function toBe(value) { + return new Promise(resolve => { + initial.value = value + resolve(initial.value) + }) + } + + return { + toBe + } +} + +async function increase() { + count.value = 0 + setInterval(() => { + count.value++ + }, 1000) + await until(count).toBe(3) + console.log(count.value === 3) // Make sure the output is true +} +</script> + +<template> + <p @click="increase">Increase</p> +</template> +``` + +## Web Components + +### 自定义元素 + +```vue +<script setup lang="ts"> +import { onMounted, defineCustomElement } from 'vue' + +/** + * Implement the code to create a custom element. + * Make the output of page show "Hello Vue.js". + */ +const VueJs = defineCustomElement({ + props: { message: String }, + template: '<span>{{message}}</span>' +}) + +customElements.define('vue-js', VueJs) +onMounted(() => { + document.getElementById('app')!.innerHTML = '<vue-js message="Hello Vue.js"></vue-js>' +}) +</script> + +<template> + <div id="app"></div> +</template> +``` + +并且 vite.config.js 里要做相关设置 +相关知识点:[Vue 与 Web Components | Vue.js](https://staging-cn.vuejs.org/guide/extras/web-components.html) diff --git a/src/routes/argon.md b/src/routes/argon.md new file mode 100644 index 00000000..7f683373 --- /dev/null +++ b/src/routes/argon.md @@ -0,0 +1,67 @@ +--- +title: CSS · Argon主题的CSS修改 +created: 2022-01-16 14:04:17 +tags: + - CSS +slug: free-axure-cloud +summary: 基于最近所学,对当前Argon主题做了一些微小的调整 +lastmod: 2022-05-07T05:30:35.639Z +--- + +然后已经不用 Wordpress 了,这个主题加载太慢了(也可能是我自己的问题 + +```css +/*删掉tag图标*/ + +.fa-tags { + display: none; +} + +/*左侧菜单居中*/ +.leftbar-menu-item { + text-align: center; +} +/*删掉日历图标*/ +.fa-calendar-o { + display: none; +} + +/*说说字体统一大小*/ +.shuoshuo-date-date { + font-size: 15px; +} + +.shuoshuo-date-month { + font-size: 15px; +} + +/*banner字体改为思源宋体*/ +.banner-title.text-white { + font-family: 'Noto Serif SC', serif; +} +/*删掉左侧搜索栏*/ +.card-body.text-center.leftbar-search-button { + display: none; +} + +/*删掉左侧栏站点名称*/ +.leftbar-banner.card-body { + display: none; +} + +/*作者相关链接居中*/ +.site-author-links a { + text-align: center; +} + +/*去掉页脚卡片外形,并缩减边距*/ +#footer.card { + background: none; + padding: 0; + box-shadow: none !important; +} + +html.darkmode #footer { + background: none !important; +} +``` diff --git a/src/routes/axure.md b/src/routes/axure.md new file mode 100644 index 00000000..afe3ae2e --- /dev/null +++ b/src/routes/axure.md @@ -0,0 +1,32 @@ +--- +title: 两种免费发布Axure原型的方式 +created: 2022-01-21 00:11:17 +tags: + - 实用技巧 +slug: free-axure-cloud +summary: 通过Netlify和Vercel发布 +lastmod: 2022-04-07T07:24:20.692Z +--- + +好像很少看到有人提,这里简单记一下思路,我有用 Vercel 成功试验过,跟本地预览效果是一样的,还可以改域名。不过了解过 Netlify 之后,我觉得用 Netlify 更简单,有兴趣的可以再研究一下。 + +## Netlify + +需要了解 Netlify 的使用方式 + +1. 在 Axure 中导出文档的 HTML 文件:发布>生成 HTML 文件 +2. 注册并托管到 Netlify +3. 上传 HTML 文件:site -> 拖动 HTML 文件夹到下面的虚线区域 + +更新方式:再导入一次更新后的文件包 + +## Vercel +Github + +需要了解 Github 的基本使用方式和 Vercel + +1. 在 Axure 中导出文档的 HTML 文件:发布>生成 HTML 文件 +2. 创建一个 Github repo,并下载到本地 +3. 复制 HTML 文件到本地 Github repo 的文件夹中,并 commit->push 到云端 +4. 导入相应 repo 到 Vercel 中 + +更新方式:复制更新后的 HTML 文件夹到相应的 Github repo 文件夹中,覆盖原来的 diff --git a/src/routes/css-fundamental.md b/src/routes/css-fundamental.md new file mode 100644 index 00000000..75d6e169 --- /dev/null +++ b/src/routes/css-fundamental.md @@ -0,0 +1,471 @@ +--- +title: CSS · 基础笔记 +created: 2021-12-06T11:59:47+08:00 +slug: css-fundamental +tags: + - CSS +lastmod: 2022-05-07T05:30:27.910Z +--- + +一些随堂笔记。 + +课程:[Build Responsive Real-World Websites with HTML and CSS](https://www.udemy.com/course/design-and-develop-a-killer-website-with-html5-and-css3/) + +## css 是什么 + +- **C**ascading**S**tyle**S**heets (层叠式样式表) +- CSS describes the **visual style and presentation** of the **content written in HTML** +- CSS consists of countless **properties** that developers use to format the content: properties about font, text, spacing, layout, etc. + +## 分类 + +### inline CSS + +```html +<p style="color:blue">text</p> +``` + +> 最好不用 + +### internal CSS + +放在`<head>`里面的`<style>`,如: + +```html +<head> + <style> + h1 { + color: blue; + } + </style> +</head> +``` + +- 代码很长的时候,整理很麻烦 + +### external CSS + +- 引用单独的 css 文件,如 `style.css` +- 放在`<head>`里用`<link>`引用,如: + +```html +<head> + <link href="style.css" ref="stylesheet" /> +</head> +``` + +关于`<link>` ,可参考:[MDN](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/link#attr-rel) + +## 组成 + + + +## Selectors + +### descendent selector + +```css +footer p { + font-family: sans; +} + +article header p { + color: blue; +} +``` + +### line selector + +```css +h1, +h2, +h3 { + color: blue; +} +``` + +定义特定元素样式的两种方式:CSS ID、class attributes + +## id + +> 给每个元素一个 id,仅能用一次,尽量不要用 + +在 HTML 中: + +```html +<p id="author">text</p> +``` + +在 CSS 中: + +```css +#author { + font-family: sans; +} +``` + +### class attributes + +> 能无限复用 + +在 HTML 中: + +```html +<p class="author">text</p> +``` + +在 CSS 中: + +```css +.author { + font-family: sans; +} +``` + +> 能无限复用 + +在 HTML 中: + +```html +<p class="author">text</p> +``` + +在 CSS 中: + +```css +.author { + font-family: sans; +} +``` + +### universal selector + +- 对所有元素生效 +- 优先级最低 +- 不可继承 + +```css +* { + color: #1098ab; +} +``` + +### body + +- 在`<body>` 里的通常只应用于文本元素(text) +- Not all properties get inherited. It’s mostly ones **related to text**: `font-family`, `font-size`, `font-weight`, `font-style`, `color`, `line-height`, `letter-spacing`, `text-align`, `text-transform`, `text-shadow`, `list-style`, etc. +- 可继承 + +### 优先级 + +#### conflicting between selectors + + + +- 尽量不要用`!important` + +```css +foot p { + color: green !important; +} +``` + +- 所有 selector 都会被应用,只有冲突的按优先级应用 +- 当使用相同类别的 selector 时,应用最后一个 + +#### inheritance + +- inheriting elements can easily be override +- 优先级最低 +- body 可继承,universal selector 不可以 + +## 颜色 + +### RGB/RGBA + +- 基本`(r,g,b,alpha)` +- 白色`(255,255,255)` +- 黑色`(0,0,0)` + +## Hexadecimal Colors + +- 0 to ff (255 in hexadecimal numbers): + `#00ffff` +- Shorthand, when all colors are identical pairs + `#off` + 当需要透明度的时候才用 rgb 颜色,一般用 hex + +## Pseudo Class + +> 用来指定特定元素 + +### 第一个元素/最后一个元素 + +```css +li:first-child { + font-weight: bold; +} + +li:last-child { + font-style: italic; +} +``` + +### 奇数/偶数 /特定次序 + +```css +li:nth-child(odd) { + font-style: italic; +} + +li:nth-child(even) { + font-style: italic; +} + +li:nth-child(3) { + font-style: italic; +} +``` + +### 多种元素时 + +如下所示,当 HTML 中`<p>`并不是`<article>`里面的第一个元素时,不生效。 + +即当母元素(parent element)里有多种元素时(child elements),不宜使用伪类(pseudo class),可以在列中使用,如`<li>` + +```html +<article> + <head></head> + <p></p> +</article> +``` + +```css +article p:fist-child { + font-family: sans; +} +``` + +### Style hyperlinks + +四个状态都应定义,并按顺序排列 + +#### link + +不进行交互的预览下 + +```css +a:link { + color: #1098ad; +} +``` + +#### visited + +点击后 + +```css +a:visited { + color: #777; +} +``` + +#### hover + +悬停时 + +```css +a:hover { + color: orangered; + font-weight: bold; + text-decoration: underline dotted orangered; +} +``` + +#### active + +点击时(通常和 hover 同时出现 + +```css +a:active { + background-color: black; + font-style: italic; +} +``` + +## Pseudo Elements + +> any pseudo elements is actually an inline element + +在 HTML 中并不真实存在,但仍可在 CSS 中选择,如行内第一个字符: +在 HTML 中并不真实存在,但仍可在 CSS 中选择,如行内第一个字符: + +```css +h1::first-letter { + font-style: normal; +} +``` + +段内第一行: + +```css +p::first-line { + color: red; +} +``` + +### adjacent sibiling selector + +在同一 parent element 里,下面最临近的元素 + +如 h3 标题下的 p: + +```css +h3 + p::first-line { + color: red; +} +``` + +### after + +- 就算没有文字内容,也要定义`content` +- 位于行末 + + + +```css +h2::after { + content: 'TOP'; + font-size: 16px; + font-weight: bold; + color: black; + padding: 5px 15px; + display: inline-block; + position: absolute; + top: -10px; + right: -25px; + background-color: #ffe70e; +} + +h2 { + position: relative; +} +``` + +### before + +- 位于行前 + +```css +h2::before { +} +``` + +## Box Model + + + + + + + +### Type of boxes: + +#### inline boxes + + + +#### block-level boxes + + + +#### inline-block boxes + + + +img 其实是 inline-block box + +### padding + +内边距 + +```css +padding: 上下 左右; +``` + +#### reset margin ane padding + +```css +* { + margin: 0; + padding: 0; +} +``` + +不要用 body + +### margin + +外边距 + +#### collapsing margins + +- 当两元素的 margin 重合时,大的会覆盖小的 + +### Dimensions + +#### width + +- percentage: percentage of the width of the parents containers + +## position + + + +### Normal Flow + +- 默认状态 + +### Absolute Position + +- 绝对位置 +- 常用于一些小元素 +- parent elements/containers should set to `relative` +- 取决于最近层次的 relative parent elements/containers + + + +### centering page + +use the `<div>` element to create a container class, then set the `margin-left` and `margin-right` to `auto` + +```css +.container { + width: 800px; + margin: 0 auto; +} +``` + +## 基本操作 + +### CSS 注释 + +- `/*需要注释的内容*/` +- VS code 快捷键 :`CMD+/` (mac) `Control+/` (win) +- 调出开发者工具`cmd+i` + +### Fix bugs + +- 方法: + + - **inspect** + - 检查刚开始变得不一样的地方 + - 如果有很多 selector,复杂的那个会被首先应用 + - 检查 css 文件引用链接是否正确 + +- 工具: + HTML 校对器: + [Markup Validation Service](https://validator.w3.org/) + + 代码比对: + [Diffchecker](https://www.diffchecker.com/) diff --git a/src/routes/css-layout.md b/src/routes/css-layout.md new file mode 100644 index 00000000..83cbcc49 --- /dev/null +++ b/src/routes/css-layout.md @@ -0,0 +1,269 @@ +--- +title: CSS · 三种布局方式 +slug: css-layouts +tags: + - CSS +created: 2022-01-13T10:07:00.000Z +summary: Float / Flexbox / Grid / +lastmod: 2022-05-07T05:30:20.613Z +--- + +课程:[Build Responsive Real-World Websites with HTML and CSS](https://www.udemy.com/course/design-and-develop-a-killer-website-with-html5-and-css3/) + +## Overview + + + +## Box-sizing + + + +> 未定义之前:content-box + +### reset + +```css +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} +``` + +## Float Layout + +- 浮动在页面之上,对周围元素没有影响 +- 现在很少用了 + +### left + +- 浮动到页面左边 + + + +```css +.author-img { + float: left; +} +``` + +> 此时图片和文本不在同一个层面上 + +### right + +> 浮动到页面右边 + + + +```css +p { + float: right; +} +``` + +### ABSOLUTE POSITIONING VS. FLOATS + + + +### Clear Float + +- 当没有其他元素可以 clear float 时,在其后新建一个元素设置 clear +- 当存在需要清除的元素时,直接在该元素上清除 + +#### 方法 1: empty div + +使用一个并列的空`<div>`元素来清除 + +```html +<head> + <h1>title</h1> + <h2>title</h2> + <div class="clear"></div> +</head> +``` + +```css +/*清除两者*/ +.clear { + clear: both; +} + +/*清除左边*/ +.clear { + clear: left; +} +``` + +#### 方法 2:clearfix hack + +原理和 empty div 的方式一样,但更简单,利用 pseudo element 在 parent element 后增加一个元素 + +```html +<head class="clearfix"> + <h1>title</h1> + <h2>title</h2> +</head> +``` + +```css +.clearfix::after { + clear: both; + content: ''; + display: block; +} +``` + +## Flexbox + + + + + +> 加粗字体为默认状态 + +```css +.container { + display: flex; + align-items: center; /*水平居中*/ + justify-content: center; /*垂直居中*/ +} +``` + +### Flex container + +- flex container takes the height of the tallest items + +常用: + +```css +.container { + display: flex; + align-items: stretch; + justify-content: flex-start; +} +``` + +### Flex items + +```css +.items { + align-self: flex-start; +} +``` + +### Propetities + +#### order + +数字越大越靠后 + +#### flex-grow + +数字越大占比越大 + +#### flex-basis + +item’s width + +#### shorthand + +`flex:1` = + +```css +flex-grow: 1; +flex-shrink: 1; +flex-basis: 0%; +``` + +## CSS Grid + + + + + + + +### grid container + +```css +.container { + display: grid; + grid-template-columns: 250px 200px; /*两列*/ + grid-template-rows: 200px 200px; /*两行*/ +} +``` + +> 通常不定义 rows + +#### Gap + +用 gap 而不是 margin: + +```css +.container { + /*gap:20px;*/ + columns-gap: 30px; + row-gap: 20px; +} +``` + +#### align tracks inside containers + +> when the content is smaller than the grid + +```css +justify-content: center; +align-content: center; +``` + +### grid items + +```css +.items: { + grid-column: 2/3; /*当前后数值相差只有1位时,可省去后面的数字*/ + grid-row: 2/3; + + /*占几个列/行时*/ + grid-column: 1/3; + grid-row: 1/3; + /*或者*/ + grid-column: 1 / span 3; + /*自动填充剩余空间*/ + grid-column: 1/-1; +} +``` + +#### align items inside cells + +```css +.container { + align-items: center; + justify-items: center; +} +``` + +```css +.item { + align-self: end; +} +``` + +### fr + +- `1fr`:自动填充剩余空间 +- 2:1:1:1 的四列 + +```css +.container { + grid-template-columns: 2fr 1fr 1fr 1fr; +} +/*shorthand*/ + +.container { + grid-template-columns: repeat(4, 1fr); +} +``` + +### implicit row + +所定义的空间被用完后多出来的列 diff --git a/src/routes/flare.md b/src/routes/flare.md new file mode 100644 index 00000000..46c83a69 --- /dev/null +++ b/src/routes/flare.md @@ -0,0 +1,140 @@ +--- +title: 自建网页书签Flare +created: 2022-02-20 14:04:17 +tags: + - Docker + - Self-hosted +slug: flare +summary: 通过Docker自建网页书签Flare +lastmod: 2022-03-30T02:19:33.711Z +--- + +Flare 是一个自托管的网页书签导航 (个人理解),详细介绍可以看作者写的这篇:[使用 Docker 搭建适用于 HomeLab 的书签导航](https://sspai.com/post/71329) + +效果如图: + + +这里分成了“应用”和“书签”两个栏目,但其实都是网页链接书签,看示例文档里的设置,应用里的是使用更为频繁的链接,书签栏则是一些参考链接/外链,或许“应用”命名为“常用”,“书签”则命名为“链接”或者“其他”更好一些。我之前还以为应用是本地应用……(我的问题 +╮( ̄ ▽  ̄"") ╭ + +项目仓库:[GitHub - soulteary/docker-flare](https://github.com/soulteary/docker-flare) + +### 1.创建 Flare 文件夹 + +此处我将文件夹命名为 flare + +```bash +mkdir ~/flare && cd ~/flare +``` + +### 2.下载包含示例的代码 + +```bash +git clone https://github.com/soulteary/docker-flare.git +cd docker-flare +``` + +### 3.运行容器 + +#### 方法 1:直接启动 + +```bash +# 可以使用最新镜像 +docker run --rm -it -p 5005:5005 -v `pwd`/app:/app soulteary/flare +# 也可以追求明确,使用固定版本 +docker run --rm -it -p 5005:5005 -v `pwd`/app:/app soulteary/flare:0.2.10 +``` + +#### 方法 2:通过 docker composer + +因为示例文件夹里面已经有一个 `docker-compose.yml` 文件了,所以我们不需要另外创建,如果需要修改的话可以用 `nano docker-compose.yml` 编辑 (如果需要设置用户登陆的话,需要在此修改) + +启动容器: + +```bash +docker-compose up -d +``` + +这时我们可以通过 `http://ip:5005` 访问书签页面了。 + +### 4.修改书签内容 + +我用的是 Royal TSX 上的 File transfer 来查看文件夹内容。 + +配置文件的路径如下: + +各个文件的功能如下: + +- config.yml:基本应用设置/标题/组件…… +- apps.yml:设置“应用”栏的书签名字/链接/icon/描述 +- bookmarks.yml:设置“书签”栏的分类/链接名称/icon/链接 + +作者贴心地内置了 `@mdi/font` 相关 icon,可以通过 `http://ip:5005/resources/mdi-cheat-sheets/` 来访问图标列表,然后通过 Ctrl /CMD+ F 来进行页面内搜索。 + +在书签页面,可以通过左下角的齿轮图标调整主题和其他设置,和 `config.yml` 里的选项几乎一致: + + +### 5.用 nginx 反代和设置域名 + +安装 Nginx 并打开 `flare.conf` 文件 + +```bash +apt install nginx +cd /etc/nginx/sites-enabled/ +rm rf default +cd /etc/nginx/sites-available/ +nano flare.conf +``` + +`flare.conf` 的内容 (注意修改 example.com 为自己的域名) + +```conf +server { + listen 80; + listen [::]:80; + server_name example.com; + + location / { + proxy_pass http://127.0.0.1:5005; + } +} +``` + +用 `nginx -t` 测试配置文件,如果最后有出现 `suceessful`,那测试就成功了。 + +然后为这个配置文件增加一个链接 + +```bash +cd /etc/nginx/sites-enabled/ +ln -s ../sites-available/flare.conf flare.conf +``` + +重启 nginx + +```bash +systemctl reload nginx +``` + +刚遇到一些问题,又回到了我的 Miniflux 主页^ ^,遂重启容器: + +```bash +cd ~/flare/docker-flare +docker-compose down +docker-compose up -d +``` + +### 6.SSL 证书和其他书签 + +安装证书,详情参考这篇:[用 docker 安装 Halo 博客并用 Nginx 反代](https://halo.seviche.cc/archives/halo-in-docker) + +```bash +certbot --nginx -d example.com -d www.example.com +``` + +其他好用书签: + +- [Raindrop](https://app.raindrop.io/):可以共享的网页书签,也可以嵌入到网页中,我的白噪音书签:[白噪音](https://raindrop.io/Rainny/-21205390) +- [Airtable](https://airtable.com/):一个共享表格工具,详情可以看我的资源收藏夹:[About Coding](https://airtable.com/shrpftxf6JgRomP2X/tblEvtThXHNBMQ8lW/viwSXGTALloahC10H) +- [Guardo](https://guardo.io/):说是可以替代 Raindrop,但书签文件夹不能生成共享链接,所以一直没用。 + +再次感谢[云五的 WordPress 搭建教程](https://yukieyun.net/tech/shared-service-same-server-wordpress/),照葫芦画瓢套用了 Nginx 的设置。 diff --git a/src/routes/grid.md b/src/routes/grid.md new file mode 100644 index 00000000..904ecffc --- /dev/null +++ b/src/routes/grid.md @@ -0,0 +1,45 @@ +--- +title: CSS · Reusable Grid +slug: css-grid +tags: + - CSS +created: 2022-01-13T10:01:44.000Z +summary: 可复用的CSS Grid设置 +lastmod: 2022-05-07T05:32:52.372Z +--- + +1. 先设置一个 class 为 grid,并设定 gap +2. 子元素中再设置具体的列数 + +如设一个上为 2 列和下为 3 列的栅格布局: + +#### CSS: + +```css +.grid { + display: grid; + gap: 1.2rem; +} +.grid--2--cols { + grid-template-columns: repeat(2, 1fr); +} + +.grid--3--cols { + grid-template-columns: repeat(3, 1fr); +} +``` + +#### HTML: + +```html +<section class="grid grid--2--cols"> + <div>Text1</div> + <div>Text2</div> +</section> + +<section class="grid grid--3--cols"> + <div>Text1</div> + <div>Text2</div> + <div>Text3</div> +</section> +``` diff --git a/src/routes/halo.md b/src/routes/halo.md new file mode 100644 index 00000000..bd44fda9 --- /dev/null +++ b/src/routes/halo.md @@ -0,0 +1,275 @@ +--- +title: 用 docker 安装 Halo 博客 +created: 2022-02-16 14:04:17 +tags: + - Docker + - Self-hosted +slug: halo +summary: '' +lastmod: 2022-05-07T05:39:19.169Z +--- + +系统:ubuntn 20.04 + +参考: + +- [用 Nginx 反代实现 docker 安装 WordPress 与其他服务并存 – 沉默之沙](https://yukieyun.net/tech/shared-service-same-server-wordpress/) +- [RSS | RSSHub 搭配 Miniflux,实现订阅自由](https://mantyke.icu/2021/rsshub-miniflux/) + +Halo 官网:[Halo](https://halo.run/#quickstart) + +建议大家先去[Halo 官网主题仓库](https://halo.run/themes.html)看看有没有喜欢的主题再决定要不要装,不然很可能像我一样装了之后又跑路了…… ^^ + +## 准备 + +### 解析域名 + +域名生效需要时间,为了避免申请 SSL 证书时屡次失败导致超过申请次数限制,最好提前添加域名解析[^1],我这里用的是子域名。 + +为域名添加一个 A 记录,指向服务器所在 IP。如 Namesile 中: + + +### 安装 docker + +在 Ubuntu 中安装 docker[^2]: + +```zsh +apt update +apt install apt-transport-https ca-certificates curl software-properties-common +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - +add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable" +apt update +``` + +其他系统可参考:[Plume | 利用 Docker-compose 搭建 Fedi 开源博客平台 – Zoe's Dumpster.](https://blog.tantalum.life/posts/build-plume-in-docker/#%E5%AE%89%E8%A3%85dockerdocker-compose)中的安装方式,或者查看官方文档。有的 VPS 在装系统的时候也可以预装 docker,如 vultr/contabo 都有,不过 docker-compose 就需要自己装(这里似乎没有用到 docker-compose。 + +检查是否装好,有版本就装好了: + +```zsh +docker -v +``` + +### 检查端口开放情况 + +参考:[Ubuntu20.04 开放指定端口\_哈-CSDN 博客](https://blog.csdn.net/lianghecai52171314/article/details/113813826) + +我不太懂端口是怎么开放的,自动开放还是用了就开放。这里用的是 ufw 来设置防火墙开放端口,用 netstat 查看端口占用状况。 + +Debian/Ubuntu 中安装 netstat[^3]: + +```zsh +apt install net-tools +``` + +检查端口占用: + +```bash +netstat -aptn +``` + +ufw 是 Ubuntu20.04 系统预装的 [^3],如未安装上,可以这样安装: + +```bash + sudo apt update + sudo apt install ufw +``` + +常用操作: + +```bash +ufw enable #打开防火墙 +ufw disable #关闭防火墙 +ufw status #查看防火墙规则 +ufw allow 22 #打开22端口 +ufw deny 22 #拒绝访问22端口 +``` + +打开防火墙之前最好打开常用的端口,如 22,不然可能会连不上服务器。 + +## 安装 Halo + +参考:[使用 Docker 部署 Halo | Halo Documents](https://docs.halo.run/getting-started/install/docker) + +### 1.创建工作目录 + +创建一个文件夹存放 Halo,我这里命名为.halo,当然也可以叫其他的。 + +```bash +mkdir ~/.halo && cd ~/.halo +``` + +### 2.下载示例配置文件到工作目录 + +```bash +wget https://dl.halo.run/config/application-template.yaml -O ./application.yaml +``` + +### 3. 编辑配置文件,配置数据库或者端口 + +我不会用 vim 命令,所以这里用 nano 编辑 + +#### 打开配置文件 application.yaml + +```bash +nano application.yaml +``` + +#### 修改配置 + +[配置参考 | Halo Documents](https://docs.halo.run/getting-started/config) +这里已经有刚下载好的配置文件了,我们可以根据自己的需要修改,Halo 数据库支持 H2 和 Mysql 数据库,因为我已经安装了一个 Wordpress 博客占用了 Mysql 数据库,虽然不知道有无影响,但为了避免出错,最后选择了按示例里的配置,使用 H2 数据库(主要还是懒得改 + +我这里将端口放到 8090,因为原 80 端口已经被占用,大家可以选择其他开放端口,注意修改数据库用户名和密码。 + +```yaml +server: + port: 8090 + + # Response data gzip. + compression: + enabled: false +spring: + datasource: + # H2 database configuration. + driver-class-name: org.h2.Driver + url: jdbc:h2:file:~/.halo/db/halo + username: admin #数据库用户名 + password: 123456 #数据库密码 + + # MySQL database configuration. + # driver-class-name: com.mysql.cj.jdbc.Driver + # url: jdbc:mysql://127.0.0.1:3306/halodb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + # username: root + # password: 123456 + + # H2 database console configuration. + h2: + console: + settings: + web-allow-others: false + path: /h2-console + enabled: false + +halo: + # Your admin client path is https://your-domain/{admin-path} + admin-path: admin + + # memory or level + cache: memory +``` + +### 4. 拉取最新的 Halo 镜像 + +```bash +docker pull halohub/halo:latest +``` + +### 5. 创建容器 + +```bash +docker run -it -d --name halo -p 8090:8090 -v ~/.halo:/root/.halo --restart=unless-stopped halohub/halo:latest +``` + +### 6. 打开安装引导界面。 + +访问 `http://服务器ip:端口号` + + +## 用 Nginx 反代 + +说实话,我现在还没懂 Nginx 是干嘛的,反代又是什么,但好像一般都要有,那就做一下吧。幸好 Halo 还有现成的配置[^4]可以抄一下,结合云五的 Wordpress 搭建教程[^2],把里面 wordpress 的部分改为 halo 就可以了。 + +### 1.安装 Nginx + +```bash +apt install nginx +cd /etc/nginx/sites-enabled/ +rm rf default +cd /etc/nginx/sites-available/ +nano halo.conf +``` + +### 2.配置 halo.conf + +注意修改`www.yourdomain.com` 为自己的域名 + +```conf + upstream halo { + server 127.0.0.1:8090; + } + server { + listen 80; + listen [::]:80; + server_name www.yourdomain.com; + client_max_body_size 1024m; + location / { + proxy_pass http://127.0.0.1:8090; + proxy_set_header HOST $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + } +``` + +用`nginx -t`测试配置文件,如果最后有出现`suceessful` ,那测试就成功了。 + +然后为这个配置文件增加一个链接 + +```bash +cd /etc/nginx/sites-enabled/ +ln -s ../sites-available/halo.conf halo.conf +``` + +### 3.重启 nginx + +```bash +systemctl reload nginx +``` + +现在访问域名就可以到达 halo 博客主页了,如果不行,可以 ping 一下域名看是不是解析还没生效:如 `ping exampl.com` + +我这里出现一个问题,是输入域名后到了我的 miniflux 主页,因为 halo.conf 里域名后缀输错了…… + +## 安装 certbot,为域名获取免费 SSL 证书 + +有 ssl 证书后,就不会被提示网站不安全了,也就是从 http->https + +### 1.安装 certbot + +```bash +apt install certbot python3-certbot-nginx +``` + +### 2.配置证书 + +修改 example.com 为自己的域名: + +```bash +certbot --nginx -d example.com -d www.example.com +``` + +如果你不确定是否可以申请成功,或者还在修改/测试配置,可以在后面加`--staging` 测试,避免申请超过次数限制[^1] + +一些选项,这里最好选择 1,如果选择 2,就没办法用 http 访问域名了: + +```text + 1: No redirect - Make no further changes to the webserver configuration. + 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for + new sites, or if you're confident your site works on HTTPS. You can undo this + change by editing your web server's configuration. +``` + +然后同意条款后问是否暴露邮箱时选 No[^2]。 + +## 其他 + +- 主题编辑:首页 / 外观 / 主题编辑 里修改具体文件 +- 进入后台:域名后面加/admin,如:https://yourdomain/admin + +--- + +[^1]: [WordPress + VPS 建站教程 - 少数派](https://sspai.com/post/66447#:~:text=sudo%20swapon%20/swapfile-,SSL%20%E8%B6%85%E8%BF%87%E4%BD%BF%E7%94%A8%E9%A2%91%E7%8E%87%E9%99%90%E9%A2%9D,-%E6%98%AF%E7%9A%84%EF%BC%8C%E5%85%8D%E8%B4%B9) +[^2]: [用 Nginx 反代实现 docker 安装 WordPress 与其他服务并存 – 沉默之沙](https://yukieyun.net/tech/shared-service-same-server-wordpress/) +[^3]: [如何在 Linux 中安装 netstat 命令 - 云+社区 - 腾讯云](https://cloud.tencent.com/developer/article/1852241) +[^4]: [使用 Docker 部署 Halo | Halo Documents](https://docs.halo.run/getting-started/install/docker#nginx) diff --git a/src/routes/hover.md b/src/routes/hover.md new file mode 100644 index 00000000..621b2015 --- /dev/null +++ b/src/routes/hover.md @@ -0,0 +1,32 @@ +--- +title: CSS · hover时图片放大的动效 +created: 2022-01-13T16:03:47.000Z +slug: hover-image-scale +tags: + - CSS Trick + - CSS +summary: 通过transform设置 +lastmod: 2022-05-07T05:30:52.029Z +--- + +思路: + +1. 设置放大 +2. 隐藏溢出 + +```css +.gallery-item { + overflow: hidden; +} + +.gallery-item img:hover { + transform: scale(1.1); +} +.gallery-item img { + display: block; + width: 100%; + transition: all 0.4s; +} +``` + +效果:https://codepen.io/sevichee/pen/wvrRjjq diff --git a/src/routes/html-basis.md b/src/routes/html-basis.md new file mode 100644 index 00000000..b6bfaccc --- /dev/null +++ b/src/routes/html-basis.md @@ -0,0 +1,141 @@ +--- +title: HTML · 基础笔记 +created: 2021-12-06T11:59:47+08:00 +slug: html-notes +tags: + - HTML +lastmod: 2022-05-07T05:30:47.655Z +--- + +课程:[Build Responsive Real-World Websites with HTML and CSS](https://www.udemy.com/course/design-and-develop-a-killer-website-with-html5-and-css3/) + +## HTML 是什么 + +HyperTextMarkupLanguage +[HTML 元素参考 - HTML(超文本标记语言) | MDN](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element) + +## 基本组成 + +以`<p>Hello!</p>`为例子: + +1. opening tag:`<p>` +2. content : `Hello!` +3. closing tag: `</p>` + +## 框架 + +```html +<!DOCTYPE html> +<html> + <head lang="语言代码"> + <meta charset="UTF-8" /> + <title>这是标题的位置,head里面的东西都看不见(一般来说)</title> + </head> + + <body> + <h1>一级标题</h1> + </body> +</html> +``` + +## attributes + +- 用来描述元素 elements +- pieces of data + +| attributes | | +| ---------- | --------------------------------------------------------------------------------------------- | +| src | sources | +| alt\* | alternative text (describe what the image are ) good for SEO/blind people | +| width | 宽度 | +| height | 高度 | +| href | 超链接 `#`表示回到页首 | +| target | 在哪个窗口打开\_blank 新窗口 `_self` 旧窗口 `_top`顶部 `_parent` 父级窗口,如果有窗口层级的话 | + +## tags + +| | | +| ---------- | --------------------------------------------------------------------- | +| head | 在页面中不可见的元素,如页面标题、link to css files…… | +| main | 文章的主要内容(HTML5 中新增) | +| body | 页面的主要内容 | +| section | 区块(semeantic HTMl | +| h1 | 标题,一个页面只能有一个 h1 | +| p | 段落 | +| span | 行内文本 | +| `<!— —>` | 注释 | +| b | **加粗**,和`<strong>`不同,它没有语意,是过时的表述 | +| strong | **加粗**,表示是页面中重要的元素 | +| i | _斜体_,过时的表述,应用`<em>` | +| em | _斜体_,emphasize | +| ol | 1. order list 数字排序 | +| ul | . unorder list | +| li | list item 列 ,用在 ol/ul 里面 | +| img | 图片 特殊类型,不需要包含内容,需要 attributes( src/alt/width/height) | +| meta | data about data meta charset=”UTF-8” | +| a | anchor 超链接(attribute:href) | +| header | 页头(container | +| nav | 导航 ( container | +| menu | 菜单,web application 常用 | +| article | 内容(container | +| footer | 页脚(container | +| div | 无意义内容区块 | +| br/ | 断行 | +| aside | 次级信息/额外信息 | +| figure | 常用于 feature cards/coding list…… | +| figcaption | 图片脚注(只能在 figure 里面用) | +| form | 表单 | +| input | 输入框 | +| label | 表单输入标题(也许 | +| table | 表格 | +| thead | 表头 | +| tbody | 表体 | +| th | 表头单元格 | +| tr | table row | +| td | table data | +| address | 地址 | +| s | 删除 | +| blockquote | 引言 | + +- `<button>` 和`< a>` 的区别: + - button :一种页面内操作 + - a: 页面跳转 + +## HTML entity + +特殊符号速查表 + +[Glyphs | CSS-Tricks](https://css-tricks.com/snippets/html/glyphs/) + +## semantic HTML + +- use semantic elements instead of others, such as `<div>` / `<b>` +- semantic HTML good for SEO and accessibility + +## 空格 + +` ` + +## 当 div 中用 css 设置了图片时 + +```html +<div class="image" role="img" arial-label="description about the image"></div> +``` + +## 随堂练习 + +### 商品卡片 + +<iframe height="300" style="width: 100%;" scrolling="no" title="html2" src="https://codepen.io/sevichee/embed/yLzErOL?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"> + See the Pen <a href="https://codepen.io/sevichee/pen/yLzErOL"> + html2</a> by Sowhere (<a href="https://codepen.io/sevichee">@sevichee</a>) + on <a href="https://codepen.io">CodePen</a>. +</iframe> + +### 分页器 pagination + +<iframe height="300" style="width: 100%;" scrolling="no" title="pagination" src="https://codepen.io/sevichee/embed/ZEXqREG?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"> + See the Pen <a href="https://codepen.io/sevichee/pen/ZEXqREG"> + pagination</a> by Sowhere (<a href="https://codepen.io/sevichee">@sevichee</a>) + on <a href="https://codepen.io">CodePen</a>. +</iframe> diff --git a/src/routes/insight.md b/src/routes/insight.md new file mode 100644 index 00000000..e0758dc2 --- /dev/null +++ b/src/routes/insight.md @@ -0,0 +1,154 @@ +--- +title: 什么是设计洞察 · What is Insight +created: 2021-10-15T15:07:14.533Z +tags: + - Design Thinking + - 翻译 +keywords: + - insight +--- + +原文: [thrivethinking.com](https://thrivethinking.com/2016/03/28/what-is-insight-definition/) + +## insight 不是什么 + +- 不是数据 +- 不是简单的事实观测 +- 不是用户的需求陈述(例如: 我需要、我想要) + +## 是什么 + +1. 是揭示本质的 + +> An unrecognized fundamental human truth that reveals the inner nature of things +> +> 一个未被承认的人类基本真理,揭示了事物的内在本质 + +2. 是一种看待世界的新方式 + +> A new way of viewing the world that causes us to reexamine existing conventions and challenge the status quo. +> +> 一种看待世界的新方式,使我们重新审视现有的惯例并挑战现状 + +3. 是对人类行为的深入观察 + +> A penetrating observation about human behavior thaWhat Is Insight- The 5 Principles of Insight Definitiont results in seeing consumers from a fresh perspective. +> +> 对人类行为的深入观察,导致从一个全新的角度看待消费者。 + +4. 是一种对人们潜在行为的发现 + +> A discovery about the underlying motivations that drive people’s actions. +> +> 对驱动人们行动的潜在动机的发现。 + +**洞察(Insight)让我们去思考人们为什么做事,是什么阻碍了人们做事情,它更深入问题根源,而不是停留在表面** + +## 五大要素 + +**语境/困境/动机/原因/设想** +[](http://thrivethinking.com/wp-content/uploads/2016/03/WhatIsInsight_5.jpg) + +## 定义 Insight 的步骤 + +### 1.设定语境/场景 + +> A simple observation of how people behave in a given situation, what they think, what they feel, but most importantly explain what they are doing and trying to achieve +> +> 一个关于人们在给定的情境内如何想、如何做、如何感受的观察陈述。重要的是要说明他们在做什么以及想做什么。 + +### 2.沟通困境 + +> understanding the barriers that are stopping consumers from achieving what they want to achieve with a given product, service or experience +> +> 了解消费者通过特定产品、服务或体验实现其愿望时遇到的障碍。 + +### 3. 阐明原因 + +> An insight statement is a discovery of understanding and the identification of unmet needs to explain why something is happening the way it is +> +> 洞察力声明是对理解的发现和对未满足需求的识别,以解释为什么事情会以这样的方式发生. + +> You must know the reason a consumer is behaving in a particular way, and why it is happening if you are to develop a product or service that can in some way augment the behavior or change it. +> +> 你必须知道消费者以特定方式行事的原因,以及为什么会发生这种情况,如果你要开发一种产品或服务,以某种方式增强这种行为或改变它的话。 + +### 4. 捕捉动机 + +> Discovering the underlying motivations that drive people’s actions +> +> 发现推动人们行动的潜在动机 + +> End-users of a product or service are motivated to change by the tensions that exist in their lives. +> +> 产品或服务的终端用户被他们生活中存在的紧张关系所激励而改变 + +> Look for tensions in four key areas: the physiological, the emotional, the cognitive and the environmental to inform your insight statements. +> +> 寻找四个关键领域的紧张关系:生理的、情感的、认知的和环境的, + +### 5.构想理想 + +> It is important to describe the desired end-state or situation the consumer is seeking. +> +> 描述消费者所追求的理想的最终状态或情况是很重要的 + +> The key here is not to define a solution but clearly convey how the consumer would like the world to look and feel, what the ideal experience should be. +> +> 此处的关键在于不需要定义一个清晰的解决方案,而需要清楚地传达消费者希望世界看起来和感觉如何,理想的体验应该是什么 + +> An excellent way to articulate this is to start with the statement “I wish there was, +> +> 一个很好的表达方式是以 "我希望有 "开始 + +## 如何表达 + +> Think of insight definition as a three sentence journey that takes the reader through the consumer’s situation, frustration, and future desires. +> +> 将洞察定义视为一个三句话的旅程,带领读者了解消费者的情况、挫折和未来的愿望。 + +### 第一句话——描述现状和用户行为。 + +> **_Having pictures around that instantly remind me of special moments and people, makes me feel good.”_** +> +> "身边有照片,能让我立即想起特别的时刻和人物,让我感觉很好。" + +### 第二句话——描述用户的困境,并说明造成困境的原因。 + +> **_“But I find that pictures from my digital camera often stay hidden on my devices because I never have time to print them.”_** +> +> "但是我发现我的数码相机里的照片经常被藏在我的设备里,因为我从来没有时间去打印它们。" + +### 第三句话——描述用户期望的最终理想状况。 + +> **_“I wish there was a way to enjoy them everyday without having to actively play them on my TV or computer.”_** +> +> "我希望有一种方法可以每天欣赏它们,而不必在电视或电脑上主动播放它们。" + +### Tips + +- 用第一视角书写 +- 真实、人性化的口吻 +- 客观、真诚 + +## 什么是一个好的'Insight' + +- 它在情感层面与消费者建立联系,并引起 "你显然理解我 "的反应。 +- 它重新审视现有的惯例并挑战现状。 +- 它解决了一个真正的问题,从而产生了新的客户。 +- 它通过给你一个明确的目标来激发行动。 +- 它清楚地说明了下一步该做什么以及如何为你的客户提供价值。 +- 它为团队带来了新的感知水平,并有助于做出明智的决策。 + +## Insight->How Might We? + +需要把 insight(洞察)转变为 HMW(我们如何?) + +例如: + +> "我们可能如何每天享受我们的记忆,而不必花时间在设备上主动播放它们?" + +## 后记 + +- insight 在作品集中如何表达?看到有 Insight 只写一句话的,总结为'Key insights',内容上是关键的事实发现,而跟前文所述的这种深入的洞察不同,那是否存在两种 insight 的定义写法? +- 打算把 Insight 放到 Persona 后面,一个 Persona 对应一个 insight 和一个 How Might We. diff --git a/src/routes/margin.md b/src/routes/margin.md new file mode 100644 index 00000000..c579e1e4 --- /dev/null +++ b/src/routes/margin.md @@ -0,0 +1,34 @@ +--- +title: CSS · Add margin to buttons +slug: css-margin +tags: + - CSS Trick + - CSS +created: 2022-01-13T10:04:21.000Z +summary: 用helper class为单个按钮加margin,防止元素复用时产生不必要的margin +lastmod: 2022-05-07T05:32:49.156Z +--- + +用 helper class 为单个按钮加 margin,防止元素复用时产生不必要的 margin + +## HTML + +```html +<button class="btn helper">Text</button> +``` + +## CSS + +```css +.helper { + margin-right: 1.6rem; +} +``` + +## 为按钮增加内边距 + +如果设置 border 数值为负,周边空间则会收到影响,但用 box-shadow 就不会: + +所以可以这样设置: + +`box-shadow: inset 0 0 0 3px #fff;` diff --git a/src/routes/material.md b/src/routes/material.md new file mode 100644 index 00000000..23cd0cdd --- /dev/null +++ b/src/routes/material.md @@ -0,0 +1,23 @@ +--- +title: 翻译 · Material Design3 +created: 2022-01-03 13:02:17 +tags: + - 翻译 +categories: + - 翻译 +slug: material-design-3 +summary: 部分翻译内容 +lastmod: 2022-05-07T05:30:39.510Z +--- + +上个月参与了 [Material Design 3 的中文版](https://www.yuque.com/advancedux/xr6e1n)翻译,我和搭档一起翻译了 [FAB (浮动操作按钮)](https://www.yuque.com/advancedux/xr6e1n/xip12y) 的部分内容。 + +这份翻译还存在一些问题,部分专有词汇还没统一翻译,但大概看看还是可以的。 + +相关内容: + +- 推文:[Material Design 最新改版,变化好大!](https://mp.weixin.qq.com/s/fm7RKvCSgBz0jCIWlr8mWg) +- [Material Design3 英文版](https://m3.material.io/) +- [Material Design 3 中文版](https://www.yuque.com/advancedux/xr6e1n) +- [Material Design 旧版](https://material.io/) +- [CSDC 共享语言库](https://csdcachieve.notion.site/a1fb571707784c809b508a4e63a6ce81) diff --git a/src/routes/rem-css.md b/src/routes/rem-css.md new file mode 100644 index 00000000..5747883c --- /dev/null +++ b/src/routes/rem-css.md @@ -0,0 +1,38 @@ +--- +title: CSS · Rem in CSS +slug: rem-css +tags: + - CSS +created: 2022-01-12T09:50:27.000Z +summary: CSS中的Rem是什么 +lastmod: 2022-05-07T05:32:42.708Z +--- + +默认为浏览器默认字体大小: + +``` +1rem=16px +``` + +改变 rem 为 10px: + +## 1. set px + +```css +html { + font-size: 10px; +} +``` + +> 这种方法会让用户不能改变页面字体大小,不建议用 + +## 2. percentage + +设置为用户浏览器字体的大小比例。 + +``` +html{ +/* 10px/16px =62.5%*/ +font-size:62.5%; +} +``` diff --git a/src/routes/vscode.md b/src/routes/vscode.md new file mode 100644 index 00000000..ead6966f --- /dev/null +++ b/src/routes/vscode.md @@ -0,0 +1,66 @@ +--- +title: VS Code 技巧合集 +tags: + - 实用技巧 +categories: + - 实用技巧 +slug: vscode-tips +summary: 字体/插件/设置…… +created: 2022-01-27T13:40:28.920Z +lastmod: 2022-03-30T02:20:34.584Z +--- + +<!--more--> + +## 关闭 VS Code 中的 popup 面板 + +如下图所示,在写 JavaScript 的时候,这个东西一直出来,还是蛮烦的。 + + + +解决方法:在 setting 里搜 **editor.parameterHints.enabled**,取消勾 + + + +参考来源:[visual studio code - How do I get rid of this popup in VSCode? - Stack Overflow](https://stackoverflow.com/questions/35246645/how-do-i-get-rid-of-this-popup-in-vscode) + +## 修改字体 + +改了 VS Code 里面的字体,Obsidian 里 code block 的字体也一起变了,用了有连字符的字体,箭头变得好好看! + +现在用的是 Fira Code: https://github.com/tonsky/FiraCode + +VS Code 里面可以在 settings.json 里加这行打开连字符: "editor.fontLigatures": true + +字体推荐: + +- [10 款最佳编程字体](https://zhuanlan.zhihu.com/p/36918101) +- [编程字体推荐](https://juejin.cn/post/6844904144239607821) + +## 快捷键 + +1. 按住 alt/option,可以在多个地方同时输入 +2. shift+option/alt+⬇️ 向下复制一行 +3. option/alt +⬇️/ ⬆️ 向下 / 上移动 +4. 在 html 中 输 lorem ,可以生成填充文本 + +## 插件 + +- prettier:自动美化格式 +- markdown editor:markdown 预览编辑器 +- foam:双链笔记 +- auto rename tag +- color highlight +- image preview:图片预览 +- live server:实时预览 +- [front matter](https://frontmatter.codes/):可以用来设置 hugo/hexo 等博客的 frontmatter: + +## 设置 + +- 缩进空格等于的字符数量:tab size +- 保存:auto save /format on save + +## 其他 + +- 全角半角符号区分很明显,输错全角会有波浪线提醒 +- obsidian 中有一个插件 [Open Valut in VSCode] 可以在 VS Code 里打开 obsidian 的库 diff --git a/src/routes/用户体验要素1.md b/src/routes/用户体验要素1.md new file mode 100644 index 00000000..9daf2c80 --- /dev/null +++ b/src/routes/用户体验要素1.md @@ -0,0 +1,78 @@ +--- +title: 《用户体验要素》- 用户体验为何如此重要 +summary: 第一章笔记 +created: 2021-11-29T11:06:10+08:00 +categories: + - UX学习笔记 + - 《用户体验要素》 +tags: + - 读书笔记 + - 《用户体验要素》 +hidden: false +draft: false +slug: the-elements-of-user-experience-1 +lastmod: 2022-04-16T12:55:42.468Z +# layout: post +--- + +在 MarginNote 里读了本书,现将其重新梳理一遍。 +本书共有 8 个章节,预计分 6 个文章写完,这是第一篇。 + + +## **什么是用户体验** + +> 用户体验并不是指一件产品本身是如何工作的 + +> 用户体验是指“产品如何与外界发生联系并发挥作用”,也就是人们如何“接触”和“使用”它。 + +> 用户体验设计通常要解决的是应用环境的综合问题 + +## **为体验而设计:使用第一** + +> “设计一个用户体验良好的产品"作为明确的目标,意味着不仅仅是功能成外观那么简单。 + +### 为什么小而美的产品,更容易成功? + +> 产品越复杂,确定如何向用户提供良好的使用体验就越困难。在使用产品的过程中,每一个新增的特性、功能或步骤, 都增加了导致用户体验失败的机会 + +## **用户体验和商机** + +### 为什么在网站上,用户体验比别的产品更重要? + +> 不管用户访问的是什么类型的网站,它都是一个“自助式”的产品。没有可以事先阅读的说明书、没有任何操作培训或讨论会、没有客户服务代表来帮助用户了解这个网站。 + +> 用户所能依靠的只有自己的智慧和经验,来独自面对这个网站的信息一一那么这个网站的主要目标之,就是尽可能有效地传达那些信息 + + + +### 为什么用户体验很重要?有什么作用吗 + +**商机、竞争优势** + +> 提供优质的用户体验是一个重要的、可持续的竞争优势一一不仅仅对网站是这样, 对所有类型的产品和服务也是如此。 + +> 用户体验形成了客户对企业的整体印象,界定了企业和争对手的差异,并且决定定了客户是否还会再次光顾 + +### 什么是转化率?有什么用? + +**衡量用户体验效果的指标** + + +> 转化率通过跟踪有百分之多少的用户被你“转化”到了下一个步骤,就能衡量你的网站在达到“商业目的”方面的效率有多高 + +### 改善用户体验,是为了什么?通过什么方式达到? + +> 任何在用户体验上所做的努力,目的都是为了提高效率。 + +1. “帮助人们工作得更快 +2. “减少他们犯错的几率 + +## **以用户为中心的设计** + +### 什么是“以用户为中心”的设计? + +> 创建吸引人的、高效的用户体验的方法称为“以用户为中心的设计(user- centered design)”。 + +### 怎么做? + +> 在开发产品的每一个步骤中,都要把用户列入考虑范围。 diff --git a/src/routes/用户体验要素2.md b/src/routes/用户体验要素2.md new file mode 100644 index 00000000..69ee42a8 --- /dev/null +++ b/src/routes/用户体验要素2.md @@ -0,0 +1,84 @@ +--- +title: 《用户体验要素》- 认识这些要素 +summary: 第二章笔记 +created: 2021-12-06T11:59:47+08:00 +categories: + - UX学习笔记 + - 《用户体验要素》 +tags: + - 读书笔记 + - 《用户体验要素》 +hidden: false +draft: false +slug: the-elements-of-user-experience-2 +lastmod: 2022-04-16T12:55:45.413Z +# layout: post +--- + +## **五个层面** + +- 是哪五个层面?分别有什么内容? +  + +## **表现层 (surface)** + +- 看得到,可操作 +- 组成: 在表现层( surface),你看到的是一系列的网页,由图片和文字组成。 +- 作用:一些可以点击的图片,从而**执行某种功能**,例如把你带到购物车里去的购物车图标。 + +## **框架层 (skeleton)** + +- 在表现层之下 +- 组成:是按钮、控件照片和文本区域的**位置**。 +- 作用:框架层用于优化设计布局,以达到这些元素的最大的效果和效率一一使你在需要的时候,能记得标识并找到购物车的按钮。 + +## **结构层(structure)** + +- 框架是结构的具体表达方式 +- 和框架层的区别: + - 框架层确定了在结账页面上交互元素的位置,而结构层则用来设计用户如何到达某个页面,并且在他们做完事情之后能去什么地方 + - 框架层定义了导航条上各要素的排列方式,允许用户可以浏览不同的商品分类 + - 结构层则确定哪些类别应该出现在那里。 + +笔记:相当于结构层是信息架构,框架层是信息架构所决定的布局。 地图和坐标? + +## **范围层 (scope)** + +**特性和功能**就构成了网站的范围层( scope) + +## **战略层 (strategy)** + +- 网站的范围基本上是由网站战略层( strategy)所决定的 +- 内容:包括了经营者想从网站得到什么,还包括了用户想从网站得到什么。 + +## **如何建设这五个层面?** + +### 自下而上地建设 + + + +### 在上一层完成之前开始下一层 + +上下层之间不是互相隔断的让任何一个层面中的工作都不能在其下层面的工作完成之前结束 + + + +## **基本的双重性** + +### 什么是网页基本的双重性质? + +网站既不能干脆地分类到应用程序,也不能分类到信息资源 + +### 什么是信息型的媒介类产品? + +- 关注点是信息——网站应该提供哪些信息,这些信息对用户的意义是什么 +- 创建一个富信息( information-rich)的用户体验,就是提供给用户个可以寻找、理解,且有意义的信息组合。 + +### 什么是功能型的平台类产品 + +- 关注的是任务——令所在的操作都被纳入一个过程,去思考人们如何完成这个过程。 +- 在这里,我们把网站看成用户用于完成一个或多个任务的一个或一组工具。 + +### 用户体验的基本要素是什么?(五个层次、两种类别的产品) + + diff --git a/src/routes/用户体验要素3.md b/src/routes/用户体验要素3.md new file mode 100644 index 00000000..21b45625 --- /dev/null +++ b/src/routes/用户体验要素3.md @@ -0,0 +1,87 @@ +--- +title: 《用户体验要素》- 战略层(产品目标和用户需求) +created: 2022-01-02 13:08:29 +categories: + - UX学习笔记 +tags: + - 《用户体验要素》 + - 读书笔记 +summary: 第三章笔记 +lastmod: 2022-05-07T05:33:16.860Z +--- + +- 战略层的基本问题? +  + +> 知道企业与用户双方对产品的期许和目标,有助于促进用户体验各方面战略的确立和制定 + +> 此处的关键词是“明确” + +## 产品目标 + +- 产品目标的设定需要注意什么? + > 要想在太具体和太宽泛之间取得一个平衡,我们就应该避免在尚未充分了解问题之前就试图得出结论。 + > 所以需要调研 +- 品牌识别为什么很重要? + > 对于任何一个网站,它需要明确描述的基础目标之一就是品牌识别( brand identity) + > 品牌识別——可以是概念系统,也可以是情绪反应 + > 它之所以重要是因为它无法不被用户注意。在用户与产品交互的同时,企业的品牌形象就不可避免地在用户的脑海中形成了 +- 成功标准( Success metrics)是什么?有什么用? + > 即一些可追踪的指标,如印象数、转化率、日活等 + > 在产品上线以后用来显示它是否满足了我们自己的目标和用户的需求。 + > 好的成功标准不仅影响项目各阶段的决策,也为衡量用户体验工作价值提供了具体的依据。 + - 设定成功标准需要注意什么? + > 对驱动用户体验决策而言有意义的成功标准,一定是可以明确地与用户行为绑定的标准,而这些用户行为也一定是可以通过设计来影响的行为。 + > 不是所有的成功标准必须直接由网站获得。你也可以衡量对网站的间接影响 + > 任何断章取义的标准都可能造成误导:请务必后退一步,看看除了网站之外发生了什么事,以确定你了解到事情的全貌。 + +## 用户需求 + +### 用户细分( user segmentation) + +- 什么是用户细分? + > 将用户分成更小的群组(或细分用户群) + > 每一群用户都是由具有某些共同关键特征的用户所组成。 + + + +- 如何进行用户细分? + > 经验或专业程度上的不同就形成了我们细分用户群的基本维度 + - 人口统计学 demographic) + - 性别、年龄、教育水平、婚烟状况、收入等。 + - 这些人口统计的数据概况可以相当粗略(男性:18~49 岁) 也可以非常具体(未婚、女性、大学毕业、25~34 岁、年薪 5 万美元)。 + - 消费心态档案 psychographic profile + 描述用户对于这个世界,尤其是与你的产品有关的某个事物的观点和看法的心理分析方法。 +- 细分到什么程度? + > 创建细分用户群只是一种用于“揭示用户最终需求的手段”。你真正只需要得到的是和你发现的“用户需求数目”样多的细分用户群。 +- 面对相互矛盾的用户需求应该怎么办? + > 很明显,我们无法提供一种方案可以同时满足这两种用户的需求。 + > 此时,我们要么选择针对单一用户群设计而排除其他用户群,要么为执行相同任务的不同用户群提供不同的方式。 + +### 用户研究(User Research) + +- 有什么研究工具?如何用? + > 问卷调査、用户访谈,或焦点小组) 最适合用于收集用户的普遍观点与感知。 + > 用户测试或现场调査)则更适用于理解具体的用户行为以及用户在和产品交互时的表现。 +- 什么是现场调查( contextual inquiry)? + > 现场调查是指一整套完整、有郊全面的方法,用于了解在日常生活情境中的用户行为(因而得来此名) + > 又叫行为考古 +- 什么是任务分析( task analysis)? + > 任务分析的概念是认为每个用户与产品的交互行为都发生在执行某一任务的环境中。 + > 有时任务非常具体(譬如买电影票),而有时任务比较宽泛( 如学习国际商务章程) +- 可用性的最终目标是什么? + > 所谓可用性的最终目标,都是寻找令产品更容易使用的途径 +- 什么是卡片排序法?( card sorting) + 用途 + > 对于由信息动的产品,卡片排序法( card sorting)用于探索用户如何分类或组织各种信息元素 + > 方法 + > 给用户一沓索引卡片,每一张卡片附有信息元素的名字、描述,一张图像或内容的类型。然后用户根据小组或类别,依照自己感到最自然的方式将卡片排列出来。分析几位用户的卡片排列结果,就可以帮助我们了解用户对产品信息的看法 + > 常用于信息架构 + +## 团队角色和流程 + +- 团队中怎样沟通传递产品目标和用户需求? + > 产品目标和用户需求经常被定义在一个正式的战略文档 ( strategy document)或愿景文档( vision document)中。 + > 这文档不仅仅是列出目标清单一一它提供不同目标之间的关系分析,并且说明这些目标要如何融入更大的企业环境中去。 + > 这些目标和对它们的分析经常由决策者、普通员工,和用户自己的直接意见来支持。 + > 这些意见生动地说明了项目中的战略制定问题。用户需求有时被记录在一个独立的用户调研报告中(将所有信息集中在一个地方有某些好处)。 diff --git a/src/routes/设计心理学1-1.md b/src/routes/设计心理学1-1.md new file mode 100644 index 00000000..79c8dc33 --- /dev/null +++ b/src/routes/设计心理学1-1.md @@ -0,0 +1,75 @@ +--- +title: 日用品心理学 +tags: + - 设计心理学1 + - 读书笔记 +categories: + - 设计心理学 +keywords: + - 设计心理学 +created: 2021-10-20T12:28:00.000Z +# layout: post +summary: 《设计心理学 1》第一章——好设计有两个特征可视性和易通性 +lastmod: 2022-04-16T12:55:08.323Z +--- + +最近参加了一个知识星球的读书打卡活动,每天需要阅读三个小节并归纳。于是第二次翻开这本书,不过这次决定在微信读书上阅读,划线标注之后再在 flomo 里写打卡内容,等读完了再放到 obsidian 中进行概念梳理。 + +以下在 flomo 中总结的内容 + +## 1.复杂的现代设备 + +1. 工程师思维强调逻辑而容易忽略用户 +2. 需要同时考虑人和机器 +3. 不同的设计学科有不同的侧重点,列举了工业设计、体验设计、交互设计 + +## 2.以人为本的设计 + +1. 以人为本的设计是一种设计理念,不是一门学科,和专业设计角色不同。 +2. 以人为本的设计通过分析用户的需求、能力和行为,用设计来满足用户的需求、能力和行为方式。 +3. 以人为本的设计需要通过观察来理解用户 +4. 快速测试可以方便地找到设计的问题所在。 + +## 3.交互设计的基本原则 + +- 五个心理学基本概念:示能,意符,约束,映射、反馈 +- 第六原则:概念模型 + +### 示能 + +- 物理对象和人之间的关系、物品的预设用途(能做什么) +- 示能和反示能都应被揭示出来 + +### 意符 + +- 示能的外在表现、符号提示功能 (表明在哪里操作) +- 简单的物品不应有过多的外在意符(如门) + +### 约束 + +- 对用户操作的限制。(如死锁) + +### 映射 + +- 两组要素之间的关系(如哪个开关开哪个灯) +- 不同文化之间的映射也许会不同 + +### 反馈 + +- 一些让你知道系统正在处理你的要求的方式,即系统和用户的“沟通”(如电话接通之前的嘟嘟声,) +- 需要即使反馈,只提供必要信息、避免过多反馈。 +- 需要精心策划、考虑优先权 + +## 4.系统映像 + +1. 是指设计师提供给用户的适用信息组合,如(说明书、操作说明视频等) + +2. 是一种沟通中介,设计师通过“系统映像”给用户传递产品预设的心理模型,并期望用户基于此建立一致的心理模型。 + +## 5.科技的悖论 + +产品的复杂性增加,更难学难用。作者认为最好的方式是建立统一的标准。 + +## 6.设计的挑战 + +设计需要跨学科合作,设计管理很重要,需要兼顾平衡多方目标 diff --git a/src/routes/设计心理学1-2.md b/src/routes/设计心理学1-2.md new file mode 100644 index 00000000..abf57a17 --- /dev/null +++ b/src/routes/设计心理学1-2.md @@ -0,0 +1,214 @@ +--- +title: 日常行为心理学 +tags: + - 设计心理学1 + - 读书笔记 +categories: + - 设计心理学 +keywords: + - 设计心理学 +draft: false +# layout: post +created: 2021-10-20T16:14:47.371Z +summary: 《设计心理学 1》第二章——人们如何做事?当事情出错了怎么办?怎么知道要做什么? +lastmod: 2022-04-16T12:55:05.400Z +--- + +## 1.人们如何做事:执行与评估的鸿沟 + +当人们使用物品时,会面对两个心理鸿沟:执行的鸿沟、评估的鸿沟 + + + +### 执行的鸿沟: + +- 试图弄清楚如何操作 +- 当运行正常时,会有可见的要素以消除执行的鸿沟(即是容易看得明白是如何操作的) + +### 评估的鸿沟: + +- 试图弄清楚操作的结果 +- 容易弥合(即可以从多方面获得评估的结果?) +- 评估的过程反映了努力的程度,人们必须对设备的物理状态做出解释,以便确定是否已经达到自己的期望和意图。 +- 当设备以方便的形式提供了它的状态信息,而且容易阐释,符合用户认知系统的方法,那么评估的沟壑就小。 +- 消除评价沟壑的主要的设计元素:反馈、概念模型。 + +### 如何消除这两个心理鸿沟: + +- 执行的沟壑:意符、约束、映射、概念模型 +- 评价的沟壑:反馈、概念模型 + +## 2.行动的七个阶段 + +### 行动的两个步骤 + +执行动作-> 评估结果(解释) + +### 七个阶段: + + + +#### 执行桥 (目标->外部世界) + +1. 计划 +2. 确认 +3. 执行 + +#### 评估桥(外部世界->目标) + +4. 对比 +5. 诠释 +6. 感知 + +大部分行动不需要按顺序经历所有阶段,事件行动之间相互影响,可能会有很多行动分支 + +### 行动的分类: + +- 目标驱动型(目标->) +- 事件驱动型(外部世界->) + +> 日常行动中许多是机会主义的行动,没有明确的目标 + +### “根本原因分析” + +反复思索,追问背后的真实原因,如 5w + +## 3.人的思想:潜意识主导 + + + +### 潜意识 + +- 人的行为大多数是潜意识的 +- 毫不费力 +- 基于过去的经验 +- 自动迅速进行 +- 偏重规则和结构 +- 无法区分常见与罕见 +- 不能被蓄意操纵 + +### 有意识的思维 + +- 缓慢而吃力 +- 比较、判断、解释 + +### 认知和情感 + +- 认知:提供理解 +- 情感:价值判断 +- 关系: + - 认知引导情绪,情绪影响认知 + - 协同工作 + +## 4.人的认知和情感 + +三个层次:本能、行为、反思 + +### 本能层 + +- 最基本的处理层、直接的感知 +- 快速的、自发的、潜意识的 +- 对当前状况的反映,不涉及过去 +- 简单评估,不做价值判断 + 例如:”悦耳“的声音,“冰冷”的水 (设计美学) + +### 行为层 + +- 学习能力之本、交互之本,与行动,具体的操作有关 +- 潜意识的 +- 设计:行动应与期望相关联(正价反应、负价反应),方法是建立合理的反馈机制以管理预期。 + +### 反思层 + +- 有意识的认知之本,深度而缓慢的 +- 发展深层理解,推理、判断 +- 所产生的情绪最持久 +- 反思的回忆比现实更重要,反思的情感认知会掩盖行为层和本能层带来的影响。 + +> 高层次的反思认知可以触发低层次的情绪。低层次的情绪会引发更高层次的反思认知。 + +## 5.行动的七个阶段和大脑的三个层次 + + + +### 心流 + +是什么:完全沉浸在行动中的情感 + +会如何:人们会忽略外部时间和空间,有身临其境的感觉 + +特点 + +- 人们热衷于的 +- 需要一定专注力 +- 适当难度 +- 目标清晰 +- 立即反馈 +- 控制感 +- 行动时,忧虑消失 +- 活动时,主观时间感知改变 + +## 6.自说自话 + +人们喜欢为事情建立因果关系 + +### 概念模式 + +- 是一种形式的故事 +- 来源于部分事实和经验,不是完全准确的 +- 当外部信息缺乏时,人们靠想象力辅佐建立概念模型 + +## 7.责备错误之事 + +- 人们习惯为相继发生的两件事情之间建立因果关系 +- 第一次尝试失败后,人们倾向于重复这个动作(反馈应迅速) + +### 给用户有保留的预测: + +- 显示最差的预计情况 +- 最长的预估时间 + +### 习得性无助(自责循环) + +- 指在多次经历失败后,便认为自己无法做好某事,结果陷入无助的状态。 +- 人们将不再进行尝试。 + +### 积极心理学 + +即一种正面思考的并且自我感觉良好的文化 + +### tips: + +- 不要责怪用户的错误使用,反思产品问题,提升体验 +- 比“错误处理指南”更重要的是,消除产品本身错误。 +- 直接从帮助和指导信息中纠正问题,不要让用户重新开始。 +- 为用户纠正问题提供应有的帮助。 + +## 8.不当的自责 + +### 自责的原因 + +系统差错被归因为人为差错 + +### 设计师应如何 + +- 错误发生之前:减少不当行为发生的机会->示能、意符、映射、约束等 +- 错误发生时:让人们发现错误并纠正->反馈、概念模型 + +> 人擅长灵活的工作和创造,机器擅长准确的工作。 + +## 9.行动的七个阶段:七个基本设计原则 + + + +### 前馈 + +有助于回答执行类(做)的信息->如何做? + +### 反馈 + +有助于理解发生了什么的信息->发生了什么? + +### 设计的七个基本原则 + +可见性、反馈、概念模型、示能、意符、映射、约束 diff --git a/src/routes/设计心理学1-3.md b/src/routes/设计心理学1-3.md new file mode 100644 index 00000000..87b510e1 --- /dev/null +++ b/src/routes/设计心理学1-3.md @@ -0,0 +1,137 @@ +--- +draft: false +title: 头脑中的知识和外界知识 +tags: + - 设计心理学1 + - 读书笔记 +categories: + - 设计心理学 +keywords: + - 设计心理学 +created: 2021-10-20T17:08:47.371Z +summary: 《设计心理学 1》第三章——并非精确行为需要的所有知识都得储存在头脑里 +lastmod: 2022-04-16T12:55:13.235Z +# layout: post +--- + +并非精确行为需要的所有知识都得储存在头脑里。它可以分布在不同地方——部分在头脑里,部分在外部世界,还有一部分存在于外界约束因素之中。 + +## 1. 含糊的知识引导精确的行为 + +### 人们不需要完全精确的知识来支撑引导他们的行为。 + +**原因:** + +- 知识存储在头脑和外部世界中 (知识很多) +- 行动不需要完全精确的知识 (要求不高) +- 外界世界存在自然约束(选择少了) +- 头脑中的文化规范与习俗知识减少了选择的范围。(选择少了) + +### 两种类型的知识: + +1. 陈述性(是什么)->未必是真的 +2. 程序性(怎么做)->通过练习来学习 + +--- + +- 人们利用环境获得大量备用信息:便利贴、信号灯、指示器,以及利用物品空间位置来提醒事件。 +- 我们习惯寻找特色来区分事物,经验会影响我们如何区分 + +## 2. 记忆是储存在头脑中的知识 + +复杂的密码增加了人们记忆的难度,于是人们采用简单粗暴的方式来记下密码,这反而让密码不安全了。更好的方式是使用多种标识符。 + +## 3. 记忆的结构 + +### 两种记忆类型 + +#### 1. 短时记忆(工作记忆) + +- 与当下任务有关 +- 容易受干扰 +- 用保留记忆的时常来衡量 +- 记忆难度主要受条目数量影响,一般是 5 ~ 7 个,设计时则要缩减。 +- 设计时可以利用多感官传递信息,减少对用户短时记忆的干扰 + +#### 2. 长时记忆(LTM /long-term memory) + +- 存储的是过去的信息 +- 存储和还原需要花费更多的时间和精力 +- 解释信息的方式很重要,记忆的难点在于组织和管理信息 +- 可以被伪造,回溯记忆不是完全准确的,我们只关注自己在意的信息。 + +--- + +- 合理的信息结构可以帮助理解、简化记忆,为信息的存储和提取建立了路径 +- 设计应提供有意义的信息结构,减少人们需要记忆的分量和难度。 + "帮助人们记忆的最有效方式就是使人们不需要记忆。" + +## 4.近似模型:现实世界里的记忆 + +- 现实生活中,通常不需要绝对的真理,“差不多“就可以满足人们的需求。 +- 简化的概念模型可以减轻人们的思维负担 + +## 5.头脑中的知识 + +外界知识是帮助记忆的有力工具,关键是要在合适的场合、时间 + +### 前瞻记忆 + +仅仅指记住在未来某个时间要从事的一些活动这个记忆任务。(记住未来的某一件事) + +### 未来记忆 + +指规划能力,想象未来的能力 + +### 提醒的两个层面: + +1. 信号:有件事要做 +2. 信息:这件事是什么 + +理想的提醒方法应兼顾两个层面 + +## 6.外界知识和头脑中知识的此消彼长 + +- 存储在外界和头脑中的知识,特性不同,使用的场所也不同 +- 外界的知识可以减少头脑的负担,但使用起来比较低效 + +### 外界的知识 + +查找低效、初次使用时易用性高->设计可优化信息查找效率 + +### 头脑中的知识 + +查找高效、初次使用时易用性低(需要学习)->设计可构建合理概念模型,简化学习过程 + + + +## 7.多个大脑里和多个设备中的记忆 + +- 外界知识结合头脑知识的两种记忆形式:交换记忆、数码记忆 +- 外界知识真假难辨,需要结合头脑中的知识进行辨别 + +## 8.自然映射 + +> 映射是结合外部世界与头脑里知识的最佳案例 + +### 什么是自然映射 + +作用于控制与被控制对象之间的,显而易见的映射关系, + +### 三个层次的映射 + +1. 最佳映射:直接对应 +2. 次好映射:位置靠近 +3. 第三好的映射:空间分布一至 + +### 糟糕的映射 VS 好的映射 + +- 糟糕的映射:增加记忆负荷和使用者的出错几率(常见错误:缺乏标识) +- 好的映射:不需要另外的图表和标注 + 购买者通常不是最终用户 + +## 9.文化与设计:自然映射随文化而异 + +- 对文化模式的选择将指导相对应的交互设计 +- 受文化影响的一些映射关系:时间流动的方向、书写的方向、前后关系、物品的移动方向等 +- 不同的概念模式切换时,设计会比较困难,用户需要时间学习和适应新的模式 diff --git a/src/routes/设计心理学1-4.md b/src/routes/设计心理学1-4.md new file mode 100644 index 00000000..db601d3f --- /dev/null +++ b/src/routes/设计心理学1-4.md @@ -0,0 +1,170 @@ +--- +title: 知晓:约束、可视性和反馈 +tags: + - 设计心理学1 + - 读书笔记 +categories: + - 设计心理学 +keywords: + - 设计心理学 +created: 2021-10-22T20:21:44.000Z +summary: 《设计心理学 1》第四章——遇到一个新的设备或状况,设计师应如何提供重要信息,以便人们知道如何操作 +lastmod: 2022-04-16T12:55:16.673Z +# layout: post +--- + +## 章前 + +### 外界的知识: + +- 示能、意符 +- 显示、操纵的位置 +- 操作和结果之间的匹配关系、 +- 物理约束 + +### 头脑中的知识 + +- 概念模型 +- 对行为的文化面向、语义面向和逻辑的约束 +- 现状与以往经验之间的类比 + +## 1. 四种约束因素:物理、文化、语义和逻辑 + +### 物理约束 + +- 将可能的操作局限在一定范围内 +- 物品的外部特性决定了它的操作方法 +- 不需要专门培训用户如何使用 +- 例如:电池只有朝一个方向放才可以放进去 +- Tips:重要是解决用户的基本需求,而不是“约束”本身 + +### 文化约束 + +- 文化惯例、习俗 +- 以范式的形式存在 + > 范式(schemas):知识结构,由一般规则和信息组成,主要用于诠释状况,指导人们的行为 +- 会随时间变化 +- 例如:不同地区,打招呼的方式、尺度不同 + +### 语义约束 + +- 语义:关注意义 +- 语义约束:利用某种境况的特殊含义来限定可能的操作方法 +- 依据:对现实情况和外部世界的理解 +- 会随时间变化 +- 例如:在不同地方,颜色代表的意义不同 + +### 逻辑约束 + +- 如:空间或位置上的逻辑关系约束 + +## 2. 示能、意符和约束在日常用品设计中的应用 + +### 门的问题 + +- 我们必须知道要做什么,在什么地方做(面对新事物时的操作问题) +- 利用示能、约束限制可能的操作、规避不合理的行为 +- 设计不应违反直觉(即用户习惯、思维惯性) +- 遵守文化规范 + +### 开关的问题 + +确定两类基本问题: + +1. 要控制的设备 +2. 映射 + +### 任务分析 + +通过对实际任务的细致观察,进行设计过程,从而设计出与实际任务最贴切的方法 + +### 以用户为中心的设计 + +基于对实际任务的细致观察,进行的设计过程 + +### 以活动为中心的控制 + +- 是什么:对开关所要完成的活动,进行充分仔细地分析和精心设计。以活动为划分功能/开关的依据。 +- 仍需保留手动控制、调整功能 + +## 3. 引导行为的约束力 + +**强制功能** + +### 是什么 + +一种物理约束,是较强约束的极端情况 + +### 作用 + +- 防止不当行为 +- 在行动受到限制的情况下,确保出现在某个阶段的差错不会蔓延,能够防止产生进一步的后果 + +### 例子 + +需要用钥匙才可以开车 + +### 三种强制方式 + +#### 互锁 + +- 促使行动按正确的**次序**进行(先 1>2,互相关联的操作步骤) +- 例如:必须先关闭洗衣机门,才可以开始启动洗衣机,在洗衣中途打开洗衣机门的话,会强制断电,停止洗衣。 + +#### 自锁 + +- 自锁保持一个操作停留在激活状态,防止有人过早地停止操作 +- 使某人待在一个空间,或在所需操作完成前防止误操作。 +- (将用户圈定在安全的操作空间) +- 例如:就像监狱的牢房或婴儿床的围栏,防止一个人离开那个区域。 + +#### 反锁 + +- 防止某人进入那些危险的区域,或者阻止事情的发生 +- (锁定的是危险的区域,而不是用户) +- 例如:封锁地下室的入口 + +## 4. 惯例、约束和示能 + +- 人们需要通过惯例来感知示能,对预设用途的解释是一个文化惯例 +- 遵循一致性的设计可以减轻用户的学习成本 +- 如果打破惯例得到的益处比遵循惯例得到的多,应选择打破惯例 + +## 5. 水龙头:关于设计的历史案例 + +- 标准化是设计的终极基本原则,统一的标准可以简化人们的生活,但达成一致的标准是困难的。 +- 标准应该反应用户心理上的概念模型,而不是实际的物理模型 + +## 6. 利用声音作为意符 + +### 为什么 + +- 没有办法提供可视信息时,可以利用声音提供 +- 声音可以帮助提供有用的反馈信息 + +### 应提供什么信息: + +- 声音的来源 +- 设备的工作状态 +- 不可见但对用户有用的信息 + +### 如何提供 + +- 使用自然的声音 +- 自然的声音可以反映出自然物体之间复杂的交互作用 +- 当使用人造声音时,使用丰富的声谱,同时小心提供不会恼人但信息丰富的细节 +- 设计标准 +- 报警功能 +- 定位功能 +- 减少骚扰 +- 标准化与个性化 + +### 例子 + +汽车的声音是重要的意符 + +### “拟真”(skeuomorphic) + +- 指将那些将过去的、熟悉的概念融入到新的技术里,即使它们已经不能再发挥功能了。 +- 帮助实现新旧技术之间的过渡 +- 也被翻译为“拟物化 diff --git a/src/routes/设计心理学1-5.md b/src/routes/设计心理学1-5.md new file mode 100644 index 00000000..977c7c2b --- /dev/null +++ b/src/routes/设计心理学1-5.md @@ -0,0 +1,379 @@ +--- +title: 人为差错?不,拙劣的设计 +summary: 《设计心理学 1》第五章——设计师经常严重误解人心理的局限性,要求人像机器一样工作 +created: 2021-10-27T20:22:17+08:00 +math: falase +categories: + - 设计心理学 +tags: + - 设计心理学1 + - 读书笔记 +draft: false +lastmod: 2022-04-16T12:55:20.101Z +# layout: post +--- + +- 当事故发生时,人们往往会将设计问题归因在操作的人身上,但这并不能解决问题 +- 解决问题的方法:找到根本原因,重新设计系统 + +## 1.何以出错 + +### 出错的原因 + +- 要求人们在任务和流程中做违背自然规律的事情。 +- 将系统问题归因为人的问题 +- 设计重点放在系统和设备上,忽略了使用者的需求。不了解人的身心特性,按对机器的标准要求人。 +- 时间压力 + +### 解决错误的方法 + +- 不要苛责用户,承认存在问题 +- 对问题进行**根本原因分析** + - 调查事故,直到发现单一的、潜在的根本原因。 + - 确定是什么致使人们犯错 + - 方法:5 个为什么分析法 + +### 差错之外:故意犯错 + +- 有时人们会故意犯错以达到目标 +- 不恰当的规则和流程是违规行为的一个主要原因,它不仅诱使且鼓励了违规,因为没有违规行为就不能完成工作。 + +本章主要讨论的是“无意识的犯错” + +## 2.差错的两种类型:失误和错误 + +### 差错 + +- 定义:与普遍接受的正确或合理的行为有所偏离 +- 是所有错误行为的总称 +- 两种类型:**失误** 和 **错误** + +### 差错和行动的七个阶段 + + + +- 错误发生在高水平的认知(有意) +- 失误发生在较低层次(无意) +- 记忆失效可能发生在每个阶段之间的八个转换过程中 + +### 失误 + +- 目标、计划正确 ‣ <mark>行动出错</mark> +- 分类 + 1. 行动失误 + 2. 记忆失误 + +#### 行动失误 + +- 执行了错误的动作 +- 分类: + 1. 撷取性失误(capture slips) + 2. 描述相似性失误(description-similarity slips) + 3. 功能状态失误(mode errors) + +##### 1. 撷取性失误 + +- **如何发生的** + - 某个曾经的动作挤占了需要完成的动作(陌生--更换成->熟悉) + - 有经验和技巧的人比初学者更容易犯 +- **如何避免** + - 避免有相同的起始步骤,然后再发散的流程。 + +##### 2. 描述相似性失误 + +- **如何发生的** + - 差错发生在与目标相似的对象上,在错误的对象上执行正确的动作 + - 如果对目标的描述含糊不清,就会发生描述 +- **如何避免** + - 在设计不同目的的控制和显示设备时,设计师需要确认它们之间具有明显差异 + +##### 3. 功能状态失误 + +- **如何发生的** + - 在错误的模式/功能/状态下执行操作 + - 当设备有不同的状态,而相同的控件具有不同的含义,就可能发生 + - 设备不能显示可见模式时,尤其容易发生 +- **如何避免** + - 避免模式控制的设计 + - 若无法避免,使设备能够明显地显示所激活的功能模式。 + - 设计可以抵消干扰活动对已设定模式带来影响的系统。 + +#### 记忆失误 + +原文又称:记忆失效性失误 + +- **如何发生的** + - 原打算做的行动没有做,或者没有及时评估其行动结果 + - 因记忆问题引起的差错 +- **如何避免** + - 使用最少的步骤 + - 对需要完成的步骤提供生动有效的提醒 + - 使用的强制功能 + +## 3.错误的分类 + +### 错误 + +- <mark>目标/计划出错</mark> ‣ 行动随之偏离目的。 +- 分类 + 1. 违反规则:基于正确知识->正确分析情况->_遵循错误规则_->错误行动 + 2. 缺乏知识:基于不正确或不完善的知识->_错误分析情况_->错误行动 + 3. 记忆失效:在目标、计划或评价阶段有所遗漏 + +#### 1. 基于规则的错误(违反规则) + +- **如何发生的** + - 错误地理解了问题,从而采用错误的目标或计划,导致遵循不恰当的规则。 + - 采用了正确的规则,但规则本身就有问题 + - 采用了正确的规则,但不正确地评估行为的结果。 + +> 在复杂的情况下,太多的信息就是问题所在:信息,既支持决策,也会排斥它 + +- **如何避免** + - 将当前系统状态的信息,以易于理解和阐释的方式呈现出来,以及提供必要的说明和解释。 + +#### 2. 基于知识的错误(缺乏知识) + +- 处理问题时,人类过度依赖储存在记忆中的经验,而对事物并不进行系统地分析。 +- **如何发生的** + - 当碰到异常情况,没有足够的技能或规则去处理它,人们就会采取基于知识的行为 + - 人们缺乏应对相应情境操作所需知识 +- **如何避免** + - 深入地了解状况,提供程序手册指引 + - 借助适当的概念模型来解决问题。 + - 提供良好的合作解决问题的技能和工具,如机器辅助 + +#### 3. 记忆失效的错误 + +- 记忆倾向于对一般事物进行过度概括和规范,并且过度强调事物之间的差异。 +- **如何发生的** + - 记忆出错导致*遗忘*了目标或行动计划,记忆的失误就会导致错误。 + - 某个*中断*导致人们忘记正在对目前环境状况所做的评判 +- **如何避免** + - 确保所有相关的信息连续可用,如目标、计划和对当前系统的评价 + - 假设人们在行动中可能被打断,在恢复操作时为他们提供需要的帮助。 + +## 4.社会和习俗压力 + +### 压力 + +社会和习俗压力影响大,但却难观测。好的方法是“奖励安全“、培训等等 + +> 永远不要低估社会压力对个人行为的影响力量,它可能促使原本理智的人们去做他们即使知道是错误或可能危险的事情。 + +> 社会压力不断出现。它们通常很难被记录下来,因为大多数人和组织都不愿承认这些因素,所以即使在事故调查中发现有社会压力的因素,其结果也往往隐匿不见,得不到公众的仔细监督 + +> 我们需要不同的培训;我们需要**奖励安全**,并将其置于经济压力之上 + +### 检查清单 + +> 检查清单是个功能强大的工具,经过验证,它可以增加行为的准确性和减少差错,特别是失误和记忆失效 + +- **如何使用** + - 人数:通常有两人一起作为一个团队使用检查单:一个人阅读指令,同时另外一个执行命令。 + - 清单设计:不断调整列表直到它涵盖了基本的项目,却不会额外增加负担 +- **隐患** + - 增加更多的人来检查任务 ‣ 增加了出错的机会。 + - 一些专业人士将其被视为对自己专业能力的侮辱 + - 打印清单将顺序结构强加于任务实施->增加了记忆失效的几率 (利用电子清单解决) + +## 5、差错报告 + +减少差错的唯一方法就是直面差错,承认差错存在,并为减少差错而作出改变。 +三个案例: + +### 自动化(JIDOKA) + +来源于汽车生产系统 + +- 具体: + - **立即报告差错**:当生产线上发现事情出错时->工人立即报告 + - **持续关注**:如果有故障的零件要继续移动到下一个工序->通过"安灯"停止装配线,并"报警" + - **确定原因**:技术专家聚集到问题发生区域,探寻差错发生的根本原因 + +### 防呆(POKA-YOKE) + +- 措施之一是添加简单的工具、夹具或设备来**限制**操作,避免犯错。(简单的示意) +- 需要遵循的原则:示能,意符,映射和约束,最重要的是**强迫**功能。 + +### 航空安全报告体系 + +主要是讲如何降低人们报告差错时的心理负担 + +1. 匿名提交差错报告 +2. 差错真实则豁免处罚 +3. 通过第三方机构提交差错报告检查 + +## 6.甄别差错 + +### 为什么甄别差错是困难的 + +#### 甄别失误 + +- 缺少反馈 +- 难以检测 (如记忆失效) + +#### 甄别错误 + +- 难以识别不恰当的目标->持续行动->行动和目标一致(增加信心)->接近不恰当的目标 +- 所处情境复杂 + +#### 记忆失效性(失误/错误) + +**区别:** + +- 失误:只有计划中的单一部分被漏掉 (做了不该做的) +- 错误:整个计划都被遗忘了(没有做该做的) + +### 为错误辩解 + +人们常常**忽略单一的异常情况**,并试图为其**辩解**。但他们的辩解是基于过去经验的,可能已**不适用于现状**,这些“简单处理”的辩解会让他们错失挽救错误的良机。 + +### 事故分析要置身于真实情境 + +#### 事故发生时 + +- 人们常常情绪波动、压力大 +- 所处的情境是复杂多变的 +- 没有明确的线索可以分辨关键的信息 + +#### 事故发生后 + +- 知道到底发生了什么事,遂将重点放在相关的信息上并忽略不相关的信息。 + +#### 事故分析应 + +- 调查人员应当想象自己置身于事故的参与者之中 +- 考虑操作者的所有信息,曾经接受的所有培训,以及类似的历史事件 + +## 7.为差错设计 + +### 基本原则 + +> 1. 了解差错的根本原因,通过设计以尽量减少这些诱因。 +> 2. 进行合理性检验。检查操作行为是否能够通过“一般性常识”的测试 +> 3. 设计出可以“撤销”操作的功能——“返回”以前操作,或者如果操作不能“返回”,则增加该操作的难度。 +> 4. 让人们易于发现一定会出的差错,以便容易纠正。 +> 5. 不要把操作看成是一种差错;相反,帮助操作者正确地完成动作。应该将操作认为近似于预期目的。 + +### 记忆中断 + +- 是差错的主要来源,尤其是记忆失效性差错 +- **恢复成本大**:必须记得准确的活动被打断之前的状态,目标是什么,被打断的活动处于行为周期的哪个阶段,以及当时系统的状态 +- **多任务处理**:效率低,差错更多 +- 可能的方法:设置屏蔽期、自动保存、编辑记录(足迹)等 + +### 警示信号存在的问题 + +- 设备间缺乏协同,信号互相影响,让用户分神,从而干扰问题的解决 +- 语音播报: + - 在用户视觉注意被占用时间,可以传递清楚的信息 + - 环境嘈杂时,很难听清楚 + - 干扰使用者之间的对话 + +### 为差错设计的方法 + +#### 研究差错 + +- 在差错发生前:研究如何设计预防措施 +- 是差错发生时:研究如何检测并纠正 + +#### 增加约束 + +- 对操作行为施加特殊的约束。 +- 如:约束条件、强迫性功能和防呆措施、隔离操控、使用分离模块等。 + +#### 撤销 + +- 减少差错带来的进一步影响 +- 应留有多步撤销 + +#### 差错信息确认 + +- **突出显示所有即将采取的行动和对象.** +- **突出行动的后果** +- **方法** + - 使正在操作的对象更加显眼。 + - 让操作可逆 + +#### 合理性检查 + +电子系统可以更方便的定位和确认不合理的操作,但用户并不一定能即时发现错误,在用户进行不合常规的操作时,给用户提醒、确认。 + +如:大额转账金额确认 + +#### 减小失误 + +- **撷取性失误** + - 避免中断 + - 提供恢复帮助(撤销机制) + - 尽可能让操作流程前面几步不要相似 +- **描述相似性失误** + - 保证操作及其控制尽可能不同 + - 若相似,则在物理距离上越远越好 +- **功能状态失误** + - 去掉多余功能 + - 或 :让功能彼此容易区分和明确可见 + +> 防范失误最好的办法是对正在实施的动作的特性,提供可以感受到的*反馈*,越是灵敏的反馈越能体现新的结果和状态,再伴之以能够*撤销*差错的机制 + +### 从差错到事故——瑞士奶酪模型 + + + +- 事故的发生往往有很多诱因,任何其中一个原因不出现,事故就不会发生。 +- 如何让系统更加有弹性的方法:**冗余设计和多重保护措施**: + - 设计额外的差错预防机制,减少失误、错误或设备失效的机会(奶酪上更少的孔), + - 为系统中不同的零部件设计完全不同的运行机制(努力使奶酪上的孔不要排列起来)。 + +> 我们应该好好思考系统,思考所有可能导致人为失误,进而酿成事故的交互因素,然后,策划出从总体上改进系统,使之更加可靠的方案。 + +## 8.良好的设计还不够 + +良好的设计还是难以防范人们故意犯错,差错并不全都因为设计 + +## 9.修补回复工程 + +<span class="passive">resilience engineering</span> + +### 是什么 + +- 一种应用于工业的系统管理方式 +- 目标是遇到外部冲击时,能以最小的破坏和损失恢复运转 +- 将安全视为核心价值 +- 关注于帮助人们在压力下成功应付复杂的环境以取得成功。 + +### 如何做 + +- 设置漏洞、测试工厂的反应水平 +- 反复评估,测试和改进。 +- 要持续关注于预测故障的潜在变化 + +## 10.自动化的悖论 + +### 故障时结果难以估计 + +原因: + +> 当自动化系统发生故障时,经常没有警告,人需要时间去注意问题、评估分析、解决问题。 + +### 悖论 + +> 能够执行那些枯燥乏味、令人厌烦的工作,但是不能做太复杂的工作。 + +## 11.应对差错的设计原则 + +### 人和机器协同应工作 + +> “人为差错”,往往只是一种人类特性与技术需求不相符的行动 + +- **人和机器擅长的工作不同**。人类是灵活的,多才多艺且具有创造力。机器是死板的,需要精密设置且相对局限于规定的操作。 +- **人类的能力和技术要求之间存在不匹配**,差错不可避免。 +- 设计**应正视人和机器之间的差异**,考虑到有可能出现的每一个差错,然后想办法避免这些差错, +- 设法**使操作具有可逆性**,以尽量减少差错可能造成的损失。 + +### 关键设计原则 + +- **将所需的操作知识储存在外部世界**,而不是全部储存在人的头脑中,但是如果用户已经把操作步骤熟记在心,应该能够提高操作效率。 +- **利用自然和非自然的约束因素**,例如物理约束、逻辑约束、语义约束和文化约束;利用强迫性功能和自然匹配的原则。 +- **缩小动作执行阶段和评估阶段的鸿沟**。在执行方面,要让用户很容易看到哪些操作是可行的。在评估方面,要把每一个操作的结果显示出来,使用户能够方便、迅速、准确地判断系统的工作状态。 diff --git a/src/routes/设计心理学1-6.md b/src/routes/设计心理学1-6.md new file mode 100644 index 00000000..2edddd27 --- /dev/null +++ b/src/routes/设计心理学1-6.md @@ -0,0 +1,96 @@ +--- +title: 设计思维 +created: 2021-11-03T03:49:10.353Z +categories: + - 设计心理学 +tags: + - 设计心理学1 + - 读书笔记 +draft: false +summary: 《设计心理学 1》第六章——设计需要探究问题的来源,而不只是停留在表面 +lastmod: 2022-05-07T05:31:58.650Z +--- + +这一章和第七章读得有些仓促,加上是比较熟悉的内容,所以笔记比较简短。之后梳理概念的时候再仔细写写。 + +## 1. 解决正确的问题 + +设计思维的关键是要解决真正的、根本的问题。可以通过“五个为什么”等方法去**确定问题**。“以人为本的设计”则是可以应用于**解决问题**的原则之一,它强调采用满足用户需求和能力的恰当方式去解决问题,这主要是通过四个步骤:观察->创意->打样和测试。 + +## 2. 双钻设计模式 + +描述了设计的两个阶段:找到正确的问题,和满足用户需求 + +### 四个步骤(发散->聚焦) + +1. 发现 +2. 定义 +3. 开发 +4. 交付 + +## 3. 以人为本的设计流程 + +可以将以人为本的设计原则嵌入到双钻模型中,于是有“以人为本的设计流程 + + +### 1.观察 + +- 即设计调研。通过调研深入洞察用户的真正需求,并分析用户在真实状况下的行为模式。 + +### 2. 激发创意(构思) + +有很多激发创意的方法,核心的原则是: + +- 激发大量的创意 +- 不要批评,鼓励创意 +- 质疑每一件事 + +### 3.打样(原型) + +**奥兹向导”(Wizard of Oz**): + +- 一种模拟产品功能的方法 +- 测试时让人执行通常由计算机执行的任务,且用户不知道该产品是不具备功能性的。 + +#### 4.测试 + +汇集小部分目标用户进行原型测试,作者一般一次单独测试 5 个人,有测试结果后改进方案,接着选择另外五个人再次测试。 + +- 原型测试的关键是:快速测试、快速失败 + +另外一些提到的概念:A/B 测试、以活动为中心的设计、关键点评审 + +## 4.我刚告诉你什么?那根本行不通 + +实践双钻模式和以人为本的设计理念时,会遇到很多现实的冲突。比较好的解决方法是组建跨部门的联合团队,并正视挑战,精心规划设计流程。 + +## 5.设计的挑战 + +提到了现在设计过程中常遇到的一些问题,比如有多个互相冲突的需求、缺少团队沟通的设计改动等。还提到了包容性设计/通用设计/无障碍设计的概念和意义。 + +## 6. 复杂是好事,混乱惹麻烦 + +复杂跟混乱是不一样的,复杂是“内容多,但有条理”,混乱是“内容多,但没条理”。驯服复杂性最重要的原则之一,是建立一个良好的概念模型。 + +## 7.标准化和技术 + +建立统一的标准可以提升产品的易用性,降低人们的学习成本。但建立标准是困难的,在技术没有完善之前建立标准,会让标准容易过时,而过迟建立标准,则很难达成一致。 + +## 8.故意制作困难 + +在需要设计限制的产品,比如安全系统、危险设备等,需要故意制造一些使用上的困难。但这并不意味着完全放弃产品的易用性,设计需要基于具体的任务来分析,在需要限制的地方反向利用优良设计的原则,在不需要限制的地方仍然遵循原则。 + +- 反向的规则: + +1. 隐藏关键的部位 +2. 在任务执行阶段利用不自然的匹配关系 +3. 增加操作的物理难度。 +4. 要求把握非常精确的操作时机和操作步骤。 +5. 不提供任何反馈信息。 +6. 在任务评估阶段利用不自然的匹配关系,使用户难以了解系统状态。 + +## 9. 设计:为了人类发展科技 + +设计是个非凡的学科 + +- 产品要被用户使用,这才是对设计最大的奖赏 diff --git a/src/routes/设计心理学1-7.md b/src/routes/设计心理学1-7.md new file mode 100644 index 00000000..4e075448 --- /dev/null +++ b/src/routes/设计心理学1-7.md @@ -0,0 +1,69 @@ +--- +title: 全球商业化中的设计 +summary: 《设计心理学 1》第七章——本书的总结 +created: 2021-11-03T03:59:18.000Z +categories: + - 设计心理学 +tags: + - 设计心理学1 + - 读书笔记 +slug: 24a52cec +lastmod: 2022-04-16T12:55:38.967Z +# layout: post +--- + +章前写了对本书的总结和本章的概要,提到了功能主义以及两种创新方式。 + +## 1.竞争压力 + +产品设计会受公司商业竞争压力的影响,这很可能会让设计偏离初衷,使市场产品变得同质化。设计受竞争压力影响的一个表现就是“功能蔓延”,它指的是为产品增加更多功能的倾向。 + +面对竞争压力,好的策略是“有所为,有所不为”,专心于已经占据优势的地方,让强者更强。然后聚焦于市场和宣传,大力推广已有的优势。不要盲目跟随潮流,增加无用功能。其中最重要的是关注真正在使用产品的用户的需求。 + +## 2.新技术推动变革 + +科技是巨大的变革动力,技术的发展改变了人们做事的方式,但人们的基本需求没有改变。 + +## 3.新产品上市需要多长时间 + +那些对生活有重大影响的创新,从概念到上市通常需要数十年,而且有很多的创新在上市之前就已经失败。这是因为这些创新会影响到现有的产品,会产生冲突和矛盾,用户适应新的产品也需要时间,而时间的拉长则会消耗预算和资源。 +“从概念到成功的产品,好点子需要旷日持久地跨越漫漫长途。” + +## 4.创新的两种形式:渐进式和颠覆式 + +- 渐进式创新: + - 缓慢的、持续进行的、渐进的 + - 在现有模式上的“改良” + - 随着时间的发展,可以带来显著的改变 + - “登山法” + - 大部分创新都是 +- 颠覆式创新: + - 颠覆式的全新开发 + - 完全改变现有模式 + - 通常来自于技术创新 + - 影响巨大 + - 少之又少,很难存活 + +两种都需要 + +## 5. 设计心理学:1988 ~ 2038 + +- 人们的基本需求大体上不变,科技的变革提供了截然不同的方式满足人们的需求。 +- 科技的发展,会带来文化上的改变。 +- 人机协同工作才会更强大。新技术改变了人们承担的任务,让人们更专注于自己擅长的东西、关键和重要的东西,让大脑从琐碎中解放出来。 + +## 6.书籍的未来 + +新技术的出现,让书籍、视频、音频等交互媒体的制作更加简单,这让入门变得简单,让人们更容易分享、获得信息,但这也会带来信息的泛滥,高技术水平的信息内容仍需要专业的制作。 + +## 7. 设计的道义和责任 + +设计应承担社会责任。作者批判了产品的废止制度,还提到可以采取订阅的方式可持续发展。 + +## 8. 设计思维和思考设计 + +- 只有当最终产品成功,设计才是成功的。 +- 设计必须考虑用户的整体体验。 +- 设计需要考虑许多影响因素:市场、产品的生命周期、环境成本、技术等 +- 草根的崛起:个人的力量也可以带来改变,科技为此提供了便利。 +- 技术会被改变,但设计的基本原则(比如有关可视性及反馈的原则,示能、意符、映射和概念模型的作用),不会改变,历久弥新。 diff --git a/urara/2022-07-23-notes-xss.md b/urara/2022-07-23-xss/2022-07-23-notes-xss.md similarity index 97% rename from urara/2022-07-23-notes-xss.md rename to urara/2022-07-23-xss/2022-07-23-notes-xss.md index b676d94f..f88fe069 100644 --- a/urara/2022-07-23-notes-xss.md +++ b/urara/2022-07-23-xss/2022-07-23-notes-xss.md @@ -1,12 +1,11 @@ --- +title: XSS 学习 created: 2022-07-26 tags: - XSS - Notes --- -### XSS 学习 - 介绍文章: - [前端安全系列(一):如何防止 XSS 攻击? - 美团技术团队](https://tech.meituan.com/2018/09/27/fe-security.html) diff --git a/urara/2022-08-18-lwt/index.md b/urara/2022-08-18-lwt/index.md new file mode 100644 index 00000000..069f58a2 --- /dev/null +++ b/urara/2022-08-18-lwt/index.md @@ -0,0 +1,113 @@ +--- +title: VPS · 用docker安装英语阅读工具LWT +created: 2022-08-18 +summary: Install LWT with docker and docker composer +tag: + - VPS + - Self-host + - Docker +image: /2022-08-18-lwt/wizard.png +--- + +LWT是一个英语阅读分词软件,全称叫Learning with Texts,我还没有怎么用过,详细的介绍可以看下面这几篇文章: +- [英语学习软件推荐——Learning with text (LWT) | 软通达](https://cyddgh.github.io/post/20220311154810/) +- [Learning With Text 使用全解(也许) - 知乎](https://zhuanlan.zhihu.com/p/463832139) +- [LWT(learning With Text) 本地化安装 - 知乎](https://zhuanlan.zhihu.com/p/473056398) + +LWT可以在本地安装也可以在VPS上安装,鉴于买了就要用的原则,我决定在VPS上安装看看,下面是具体的流程。 + +我所安装的docker image:[GitHub - jsz4n/lwt-docker: Learning With Texts with Docker](https://github.com/jsz4n/lwt-docker),还有试过一些其他的docker image,都不太顺利,这个 + +## 0. 准备 +前提:安装好docker 和 docker composer、nginx + +安装docker和docker composer +```bash +sudo apt update +sudo apt install docker docker-compose +sudo apt install nginx +``` + +准备DNS解析域名,如在cloudflare里面配置域名`n.example.org` :添加一个A记录,名称为`n` ,内容为VPS的ip地址,如`123.123.123.14` + +我安装了比较新的docker composer版本(1.27.4),如果没有装新版的下面操作的`docker compose `请全部替换为`docker-compose` + +## 1. 下载仓库源码 +我决定在`/opt/`文件夹下面安装,首先进入opt文件夹 +```bash +cd /opt +``` +复制git仓库并进入文件夹 +```bash +sudo git clone https://github.com/jsz4n/lwt-docker.git +cd lwt-docker +``` + +## 2. 修改配置 + +打开`docker-compose.yml`文件: +```bash +sudo nano docker-compose.yml +``` +修改密码: +```yml title="docker-compose.yml" {7,15} +version: '3' + +services: + mariadb: + image: mariadb:10.6 + restart: always + environment: + - "MARIADB_ROOT_PASSWORD=密码" #改这里 + volumes: + - ./media/:/var/lib/mysql + lwt: + image: lwt:latest + restart: always + environment: + - "MARIADB_SERVER=mariadb" + - "MARIADB_ROOT_PASSWORD=密码" #和上面的一样 + ports: + - "8080:80" #如果需要改端口的话改8080的地方 + depends_on: + - mariadb +``` + +## 3. 上线容器 +```bash +sudo docker compose up -d +``` +然后`sudo docker compose ps` 一下看`lwt-docker-lwt-1` 和` lwt-docker-mariadb-1` 这两项的情况 + +如果有错误的话,可以`sudo docker logs lwt-docker-lwt-1`看下日志 +或者试着在`/opt/lwt-docker`文件夹下创建一个media文件夹: + +```bash +sudo mkdir media +``` +然后再重新上线容器 + +## 4. 配置反代 +我比较懒,设置还是跟之前的[配置 Cloudflare 的免费 SSL 证书](2022-06-12-cloudflare)里面一样配置,如果不想按照这个方法来配置反代的话可以参考:[利用 Nginx 进行反代](https://mantyke.icu/posts/2021/rsshub-miniflux/#%E5%88%A9%E7%94%A8nginx%E8%BF%9B%E8%A1%8C%E5%8F%8D%E4%BB%A3)中的配置 + +```bash +sudo nano /etc/nginx/conf.d/lwt.conf +``` +写入 +```bash title="/etc/nginx/conf.d/lwt.conf" {2,4} +server { + listen 443 ssl; + server_name 域名; + location / { + proxy_pass http://127.0.0.1:8080; + proxy_set_header HOST $host; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} +``` +接着`sudo nginx -t` 之后 `sudo systemctl reload nginx` 重启nginx之后就可以在配置好的域名看到lwt啦 + +## 后续 +如果我有继续使用的话后面可能会更新词典配置和使用细节等内容(不确定) \ No newline at end of file diff --git a/urara/2022-08-18-lwt/wizard.png b/urara/2022-08-18-lwt/wizard.png new file mode 100644 index 00000000..855f558a Binary files /dev/null and b/urara/2022-08-18-lwt/wizard.png differ