SPA模式下的es6如何加快宿主页的显示速度
SPA的模式下,宿主页是首先加载的页面,会需要一些主要的组件,如element-plus,easyui,devextreme,ant-design等,这些组件及其依赖组件,文件多,代码量大,可能导致首页加载很慢,超过3-8秒,必须优化。文件多,浏览器与服务器之间的交互次数多,网络来回多,浪费了大量时间,如果能一次打包返回,可以提高效率。
但是SPA,不像MPA,有很多差异:
(1)SPA后续加载的作业组件,对公共组件都是内存共用的。而MPA每个页面可以不考虑共用,单独打包或tree-shaking精简。因此SPA下不能使用webpack打包模式来解决这个问题。
(2)组件的引用方式必须统一。要么大家都是引用其打包文件(如:/lodash/lodash.js),要么大家都引用零散文件(如:/lodash/目录下640个文件)。如果混用,代码在内存就不是一致的,可能引起其逻辑错误。
(3)如果想在vue下用SSR后台渲染一个dom片段直接给前端显示,很复杂:你很难在后端模拟一个与浏览器一样的环境,涉及:window环境、dom结构、vm隔离、vue组件生命周期、vue组件激活...
第三方组件大多采用模组化设计,有很多小文件:
- devextreme,devextreme-vue,700+
- lodash-es,640+
- element-plus,360+
- ant-design-vue,530+
在es6模式下,这些第三方组件一般没有提供一个打包后的大文件,一般官方建议分散使用。但它们通常又提供了一个index.mjs文件,在里面一网打尽的引用了全部小文件。如element-plus/es/index.mjs
import installer from './defaults.mjs';
export { default } from './defaults.mjs';
import './components/index.mjs';
import './constants/index.mjs';
import './directives/index.mjs';
import './hooks/index.mjs';
export { makeInstaller } from './make-installer.mjs';
export { default as dayjs } from 'dayjs';
export { affixEmits, affixProps } from './components/affix/src/affix.mjs';
export { ElAffix } from './components/affix/index.mjs';
export { alertEffects, alertEmits, alertProps } from './components/alert/src/alert.mjs';
export { ElAlert } from './components/alert/index.mjs';
export { autocompleteEmits, autocompleteProps } from './components/autocomplete/src/autocomplete.mjs';
export { ElAutocomplete } from './components/autocomplete/index.mjs';
export { avatarEmits, avatarProps } from './components/avatar/src/avatar.mjs';
export { ElAvatar } from './components/avatar/index.mjs';
export { backtopEmits, backtopProps } from './components/backtop/src/backtop.mjs';
export { ElBacktop } from './components/backtop/index.mjs';
export { badgeProps } from './components/badge/src/badge.mjs';
export { ElBadge } from './components/badge/index.mjs';
export { breadcrumbProps } from './components/breadcrumb/src/breadcrumb.mjs';
export { breadcrumbItemProps } from './components/breadcrumb/src/breadcrumb-item.mjs';
export { breadcrumbKey } from './components/breadcrumb/src/constants.mjs';
export { ElBreadcrumb, ElBreadcrumbItem } from './components/breadcrumb/index.mjs';
export { buttonEmits, buttonNativeTypes, buttonProps, buttonTypes } from './components/button/src/button.mjs';
export { buttonGroupContextKey } from './components/button/src/constants.mjs';
export { ElButton, ElButtonGroup } from './components/button/index.mjs';
export { calendarEmits, calendarProps } from './components/calendar/src/calendar.mjs';
export { ElCalendar } from './components/calendar/index.mjs';
export { cardProps } from './components/card/src/card.mjs';
export { ElCard } from './components/card/index.mjs';
export { carouselEmits, carouselProps } from './components/carousel/src/carousel.mjs';
export { carouselItemProps } from './components/carousel/src/carousel-item.mjs';
export { CAROUSEL_ITEM_NAME, carouselContextKey } from './components/carousel/src/constants.mjs';
export { ElCarousel, ElCarouselItem } from './components/carousel/index.mjs';
export { cascaderEmits, cascaderProps } from './components/cascader/src/cascader.mjs';
export { ElCascader } from './components/cascader/index.mjs';
export { CASCADER_PANEL_INJECTION_KEY } from './components/cascader-panel/src/types.mjs';
export { CommonProps, DefaultProps, useCascaderConfig } from './components/cascader-panel/src/config.mjs';
export { ElCascaderPanel } from './components/cascader-panel/index.mjs';
export { checkTagEmits, checkTagProps } from './components/check-tag/src/check-tag.mjs';
export { ElCheckTag } from './components/check-tag/index.mjs';
export { checkboxGroupEmits, checkboxGroupProps } from './components/checkbox/src/checkbox-group.mjs';
export { checkboxEmits, checkboxProps } from './components/checkbox/src/checkbox.mjs';
export { checkboxGroupContextKey } from './components/checkbox/src/constants.mjs';
export { ElCheckbox, ElCheckboxButton, ElCheckboxGroup } from './components/checkbox/index.mjs';
export { colProps } from './components/col/src/col.mjs';
export { ElCol } from './components/col/index.mjs';
export { collapseEmits, collapseProps, emitChangeFn } from './components/collapse/src/collapse.mjs';
export { collapseItemProps } from './components/collapse/src/collapse-item.mjs';
export { collapseContextKey } from './components/collapse/src/constants.mjs';
export { ElCollapse, ElCollapseItem } from './components/collapse/index.mjs';
export { ElCollapseTransition } from './components/collapse-transition/index.mjs';
export { colorPickerContextKey, colorPickerEmits, colorPickerProps } from './components/color-picker/src/color-picker.mjs';
export { ElColorPicker } from './components/color-picker/index.mjs';
export { messageConfig } from './components/config-provider/src/config-provider.mjs';
export { configProviderProps } from './components/config-provider/src/config-provider-props.mjs';
export { configProviderContextKey } from './components/config-provider/src/constants.mjs';
export { provideGlobalConfig, useGlobalComponentSettings, useGlobalConfig } from './components/config-provider/src/hooks/use-global-config.mjs';
export { ElConfigProvider } from './components/config-provider/index.mjs';
export { ElAside, ElContainer, ElFooter, ElHeader, ElMain } from './components/container/index.mjs';
export { countdownEmits, countdownProps } from './components/countdown/src/countdown.mjs';
export { ElCountdown } from './components/countdown/index.mjs';
export { ROOT_PICKER_INJECTION_KEY } from './components/date-picker/src/constants.mjs';
export { datePickerProps } from './components/date-picker/src/props/date-picker.mjs';
export { ElDatePicker } from './components/date-picker/index.mjs';
export { descriptionProps } from './components/descriptions/src/description.mjs';
export { descriptionItemProps } from './components/descriptions/src/description-item.mjs';
export { ElDescriptions, ElDescriptionsItem } from './components/descriptions/index.mjs';
export { useDialog } from './components/dialog/src/use-dialog.mjs';
export { dialogEmits, dialogProps } from './components/dialog/src/dialog.mjs';
export { dialogInjectionKey } from './components/dialog/src/constants.mjs';
export { ElDialog } from './components/dialog/index.mjs';
export { dividerProps } from './components/divider/src/divider.mjs';
export { ElDivider } from './components/divider/index.mjs';
export { drawerEmits, drawerProps } from './components/drawer/src/drawer.mjs';
export { ElDrawer } from './components/drawer/index.mjs';
export { DROPDOWN_COLLECTION_INJECTION_KEY, DROPDOWN_COLLECTION_ITEM_INJECTION_KEY, ElCollection, ElCollectionItem, FIRST_KEYS, FIRST_LAST_KEYS, LAST_KEYS, dropdownItemProps, dropdownMenuProps, dropdownProps } from './components/dropdown/src/dropdown.mjs';
export { DROPDOWN_INJECTION_KEY } from './components/dropdown/src/tokens.mjs';
export { ElDropdown, ElDropdownItem, ElDropdownMenu } from './components/dropdown/index.mjs';
export { emptyProps } from './components/empty/src/empty.mjs';
export { ElEmpty } from './components/empty/index.mjs';
export { formEmits, formMetaProps, formProps } from './components/form/src/form.mjs';
export { formItemProps, formItemValidateStates } from './components/form/src/form-item.mjs';
export { formContextKey, formItemContextKey } from './components/form/src/constants.mjs';
export { useDisabled, useFormDisabled, useFormSize, useSize } from './components/form/src/hooks/use-form-common-props.mjs';
export { useFormItem, useFormItemInputId } from './components/form/src/hooks/use-form-item.mjs';
export { ElForm, ElFormItem } from './components/form/index.mjs';
export { iconProps } from './components/icon/src/icon.mjs';
export { ElIcon } from './components/icon/index.mjs';
export { imageEmits, imageProps } from './components/image/src/image.mjs';
export { ElImage } from './components/image/index.mjs';
export { imageViewerEmits, imageViewerProps } from './components/image-viewer/src/image-viewer.mjs';
export { ElImageViewer } from './components/image-viewer/index.mjs';
export { inputEmits, inputProps } from './components/input/src/input.mjs';
export { ElInput } from './components/input/index.mjs';
export { inputNumberEmits, inputNumberProps } from './components/input-number/src/input-number.mjs';
export { ElInputNumber } from './components/input-number/index.mjs';
export { linkEmits, linkProps } from './components/link/src/link.mjs';
export { ElLink } from './components/link/index.mjs';
export { menuEmits, menuProps } from './components/menu/src/menu.mjs';
export { menuItemEmits, menuItemProps } from './components/menu/src/menu-item.mjs';
export { menuItemGroupProps } from './components/menu/src/menu-item-group.mjs';
export { subMenuProps } from './components/menu/src/sub-menu.mjs';
export { ElMenu, ElMenuItem, ElMenuItemGroup, ElSubMenu } from './components/menu/index.mjs';
export { overlayEmits, overlayProps } from './components/overlay/src/overlay.mjs';
export { ElOverlay } from './components/overlay/index.mjs';
export { pageHeaderEmits, pageHeaderProps } from './components/page-header/src/page-header.mjs';
export { ElPageHeader } from './components/page-header/index.mjs';
export { paginationEmits, paginationProps } from './components/pagination/src/pagination.mjs';
export { elPaginationKey } from './components/pagination/src/constants.mjs';
export { ElPagination } from './components/pagination/index.mjs';
export { popconfirmEmits, popconfirmProps } from './components/popconfirm/src/popconfirm.mjs';
export { ElPopconfirm } from './components/popconfirm/index.mjs';
export { Effect, popperProps, roleTypes, usePopperProps } from './components/popper/src/popper.mjs';
export { popperTriggerProps, usePopperTriggerProps } from './components/popper/src/trigger.mjs';
export { popperContentEmits, popperContentProps, popperCoreConfigProps, usePopperContentEmits, usePopperContentProps, usePopperCoreConfigProps } from './components/popper/src/content.mjs';
export { popperArrowProps, usePopperArrowProps } from './components/popper/src/arrow.mjs';
export { POPPER_CONTENT_INJECTION_KEY, POPPER_INJECTION_KEY } from './components/popper/src/constants.mjs';
export { default as ElPopperArrow } from './components/popper/src/arrow2.mjs';
export { default as ElPopperTrigger } from './components/popper/src/trigger2.mjs';
export { default as ElPopperContent } from './components/popper/src/content2.mjs';
export { ElPopper } from './components/popper/index.mjs';
export { progressProps } from './components/progress/src/progress.mjs';
export { ElProgress } from './components/progress/index.mjs';
export { radioEmits, radioProps, radioPropsBase } from './components/radio/src/radio.mjs';
export { radioGroupEmits, radioGroupProps } from './components/radio/src/radio-group.mjs';
export { radioButtonProps } from './components/radio/src/radio-button.mjs';
export { radioGroupKey } from './components/radio/src/constants.mjs';
export { ElRadio, ElRadioButton, ElRadioGroup } from './components/radio/index.mjs';
export { rateEmits, rateProps } from './components/rate/src/rate.mjs';
export { ElRate } from './components/rate/index.mjs';
export { IconComponentMap, IconMap, resultProps } from './components/result/src/result.mjs';
export { ElResult } from './components/result/index.mjs';
export { RowAlign, RowJustify, rowProps } from './components/row/src/row.mjs';
export { rowContextKey } from './components/row/src/constants.mjs';
export { ElRow } from './components/row/index.mjs';
export { BAR_MAP, GAP, renderThumbStyle } from './components/scrollbar/src/util.mjs';
export { scrollbarEmits, scrollbarProps } from './components/scrollbar/src/scrollbar.mjs';
export { thumbProps } from './components/scrollbar/src/thumb.mjs';
export { scrollbarContextKey } from './components/scrollbar/src/constants.mjs';
export { ElScrollbar } from './components/scrollbar/index.mjs';
export { selectGroupKey, selectKey } from './components/select/src/token.mjs';
export { ElOption, ElOptionGroup, ElSelect } from './components/select/index.mjs';
export { selectV2InjectionKey } from './components/select-v2/src/token.mjs';
export { ElSelectV2 } from './components/select-v2/index.mjs';
export { skeletonProps } from './components/skeleton/src/skeleton.mjs';
export { skeletonItemProps } from './components/skeleton/src/skeleton-item.mjs';
export { ElSkeleton, ElSkeletonItem } from './components/skeleton/index.mjs';
export { sliderEmits, sliderProps } from './components/slider/src/slider.mjs';
export { sliderContextKey } from './components/slider/src/constants.mjs';
export { ElSlider } from './components/slider/index.mjs';
export { spaceProps } from './components/space/src/space.mjs';
export { spaceItemProps } from './components/space/src/item.mjs';
export { useSpace } from './components/space/src/use-space.mjs';
export { ElSpace } from './components/space/index.mjs';
export { statisticProps } from './components/statistic/src/statistic.mjs';
export { ElStatistic } from './components/statistic/index.mjs';
export { stepProps } from './components/steps/src/item.mjs';
export { stepsEmits, stepsProps } from './components/steps/src/steps.mjs';
export { ElStep, ElSteps } from './components/steps/index.mjs';
export { switchEmits, switchProps } from './components/switch/src/switch.mjs';
export { ElSwitch } from './components/switch/index.mjs';
export { ElTable, ElTableColumn } from './components/table/index.mjs';
export { Alignment as TableV2Alignment, FixedDir as TableV2FixedDir, SortOrder as TableV2SortOrder } from './components/table-v2/src/constants.mjs';
export { default as TableV2 } from './components/table-v2/src/table-v2.mjs';
export { placeholderSign as TableV2Placeholder } from './components/table-v2/src/private.mjs';
export { autoResizerProps } from './components/table-v2/src/auto-resizer.mjs';
export { tableV2Props } from './components/table-v2/src/table.mjs';
export { tableV2RowProps } from './components/table-v2/src/row.mjs';
export { ElAutoResizer, ElTableV2 } from './components/table-v2/index.mjs';
export { tabsEmits, tabsProps } from './components/tabs/src/tabs.mjs';
export { tabBarProps } from './components/tabs/src/tab-bar.mjs';
export { tabNavEmits, tabNavProps } from './components/tabs/src/tab-nav.mjs';
export { tabPaneProps } from './components/tabs/src/tab-pane.mjs';
export { tabsRootContextKey } from './components/tabs/src/constants.mjs';
export { ElTabPane, ElTabs } from './components/tabs/index.mjs';
export { tagEmits, tagProps } from './components/tag/src/tag.mjs';
export { ElTag } from './components/tag/index.mjs';
export { textProps } from './components/text/src/text.mjs';
export { ElText } from './components/text/index.mjs';
export { buildTimeList, dateEquals, extractDateFormat, extractTimeFormat, formatter, makeList, parseDate, rangeArr, valueEquals } from './components/time-picker/src/utils.mjs';
export { DEFAULT_FORMATS_DATE, DEFAULT_FORMATS_DATEPICKER, DEFAULT_FORMATS_TIME, timeUnits } from './components/time-picker/src/constants.mjs';
export { timePickerDefaultProps } from './components/time-picker/src/common/props.mjs';
export { ElTimePicker } from './components/time-picker/index.mjs';
export { default as CommonPicker } from './components/time-picker/src/common/picker.mjs';
export { default as TimePickPanel } from './components/time-picker/src/time-picker-com/panel-time-pick.mjs';
export { timeSelectProps } from './components/time-select/src/time-select.mjs';
export { ElTimeSelect } from './components/time-select/index.mjs';
export { timelineItemProps } from './components/timeline/src/timeline-item.mjs';
export { ElTimeline, ElTimelineItem } from './components/timeline/index.mjs';
export { tooltipEmits, useTooltipModelToggle, useTooltipModelToggleEmits, useTooltipModelToggleProps, useTooltipProps } from './components/tooltip/src/tooltip.mjs';
export { useTooltipTriggerProps } from './components/tooltip/src/trigger.mjs';
export { useTooltipContentProps } from './components/tooltip/src/content.mjs';
export { TOOLTIP_INJECTION_KEY } from './components/tooltip/src/constants.mjs';
export { ElTooltip } from './components/tooltip/index.mjs';
export { LEFT_CHECK_CHANGE_EVENT, RIGHT_CHECK_CHANGE_EVENT, transferCheckedChangeFn, transferEmits, transferProps } from './components/transfer/src/transfer.mjs';
export { ElTransfer } from './components/transfer/index.mjs';
export { ElTree } from './components/tree/index.mjs';
export { ElTreeSelect } from './components/tree-select/index.mjs';
export { ElTreeV2 } from './components/tree-v2/index.mjs';
export { genFileId, uploadBaseProps, uploadListTypes, uploadProps } from './components/upload/src/upload.mjs';
export { uploadContentProps } from './components/upload/src/upload-content.mjs';
export { uploadListEmits, uploadListProps } from './components/upload/src/upload-list.mjs';
export { uploadDraggerEmits, uploadDraggerProps } from './components/upload/src/upload-dragger.mjs';
export { uploadContextKey } from './components/upload/src/constants.mjs';
export { ElUpload } from './components/upload/index.mjs';
export { default as FixedSizeList } from './components/virtual-list/src/components/fixed-size-list.mjs';
export { default as DynamicSizeList } from './components/virtual-list/src/components/dynamic-size-list.mjs';
export { default as FixedSizeGrid } from './components/virtual-list/src/components/fixed-size-grid.mjs';
export { default as DynamicSizeGrid } from './components/virtual-list/src/components/dynamic-size-grid.mjs';
export { virtualizedGridProps, virtualizedListProps, virtualizedProps, virtualizedScrollbarProps } from './components/virtual-list/src/props.mjs';
export { watermarkProps } from './components/watermark/src/watermark.mjs';
export { ElWatermark } from './components/watermark/index.mjs';
export { tourEmits, tourProps } from './components/tour/src/tour.mjs';
export { tourStepEmits, tourStepProps } from './components/tour/src/step.mjs';
export { tourContentEmits, tourContentProps, tourPlacements, tourStrategies } from './components/tour/src/content.mjs';
export { ElTour, ElTourStep } from './components/tour/index.mjs';
export { anchorEmits, anchorProps } from './components/anchor/src/anchor.mjs';
export { ElAnchor, ElAnchorLink } from './components/anchor/index.mjs';
export { segmentedEmits, segmentedProps } from './components/segmented/src/segmented.mjs';
export { ElSegmented } from './components/segmented/index.mjs';
export { mentionEmits, mentionProps } from './components/mention/src/mention.mjs';
export { ElMention } from './components/mention/index.mjs';
export { ElInfiniteScroll } from './components/infinite-scroll/index.mjs';
export { ElLoading } from './components/loading/index.mjs';
export { vLoading as ElLoadingDirective, vLoading } from './components/loading/src/directive.mjs';
export { Loading as ElLoadingService } from './components/loading/src/service.mjs';
export { messageDefaults, messageEmits, messageProps, messageTypes } from './components/message/src/message.mjs';
export { ElMessage } from './components/message/index.mjs';
export { ElMessageBox } from './components/message-box/index.mjs';
export { notificationEmits, notificationProps, notificationTypes } from './components/notification/src/notification.mjs';
export { ElNotification } from './components/notification/index.mjs';
export { popoverEmits, popoverProps } from './components/popover/src/popover.mjs';
export { ElPopover, ElPopoverDirective } from './components/popover/index.mjs';
export { EVENT_CODE } from './constants/aria.mjs';
export { WEEK_DAYS, datePickTypes } from './constants/date.mjs';
export { CHANGE_EVENT, INPUT_EVENT, UPDATE_MODEL_EVENT } from './constants/event.mjs';
export { INSTALLED_KEY } from './constants/key.mjs';
export { componentSizeMap, componentSizes } from './constants/size.mjs';
export { default as ClickOutside } from './directives/click-outside/index.mjs';
export { vRepeatClick } from './directives/repeat-click/index.mjs';
export { default as TrapFocus } from './directives/trap-focus/index.mjs';
export { default as Mousewheel } from './directives/mousewheel/index.mjs';
export { useAttrs } from './hooks/use-attrs/index.mjs';
export { useDeprecated } from './hooks/use-deprecated/index.mjs';
export { useDraggable } from './hooks/use-draggable/index.mjs';
export { useFocus } from './hooks/use-focus/index.mjs';
export { buildLocaleContext, buildTranslator, localeContextKey, translate, useLocale } from './hooks/use-locale/index.mjs';
export { useLockscreen } from './hooks/use-lockscreen/index.mjs';
export { useModal } from './hooks/use-modal/index.mjs';
export { createModelToggleComposable, useModelToggle, useModelToggleEmits, useModelToggleProps } from './hooks/use-model-toggle/index.mjs';
export { usePreventGlobal } from './hooks/use-prevent-global/index.mjs';
export { useProp } from './hooks/use-prop/index.mjs';
export { usePopper } from './hooks/use-popper/index.mjs';
export { useSameTarget } from './hooks/use-same-target/index.mjs';
export { useTeleport } from './hooks/use-teleport/index.mjs';
export { useThrottleRender } from './hooks/use-throttle-render/index.mjs';
export { useTimeout } from './hooks/use-timeout/index.mjs';
export { useTransitionFallthrough, useTransitionFallthroughEmits } from './hooks/use-transition-fallthrough/index.mjs';
export { ID_INJECTION_KEY, useId, useIdInjection } from './hooks/use-id/index.mjs';
export { useEscapeKeydown } from './hooks/use-escape-keydown/index.mjs';
export { usePopperContainer, usePopperContainerId } from './hooks/use-popper-container/index.mjs';
export { useDelayedRender } from './hooks/use-intermediate-render/index.mjs';
export { useDelayedToggle, useDelayedToggleProps } from './hooks/use-delayed-toggle/index.mjs';
export { FORWARD_REF_INJECTION_KEY, useForwardRef, useForwardRefDirective } from './hooks/use-forward-ref/index.mjs';
export { defaultNamespace, namespaceContextKey, useGetDerivedNamespace, useNamespace } from './hooks/use-namespace/index.mjs';
export { ZINDEX_INJECTION_KEY, defaultInitialZIndex, useZIndex, zIndexContextKey } from './hooks/use-z-index/index.mjs';
export { arrowMiddleware, getPositionDataWithUnit, useFloating, useFloatingProps } from './hooks/use-floating/index.mjs';
export { useCursor } from './hooks/use-cursor/index.mjs';
export { useOrderedChildren } from './hooks/use-ordered-children/index.mjs';
export { SIZE_INJECTION_KEY, useGlobalSize, useSizeProp, useSizeProps } from './hooks/use-size/index.mjs';
export { useFocusController } from './hooks/use-focus-controller/index.mjs';
export { useComposition } from './hooks/use-composition/index.mjs';
export { DEFAULT_EMPTY_VALUES, DEFAULT_VALUE_ON_CLEAR, SCOPE, emptyValuesContextKey, useEmptyValues, useEmptyValuesProps } from './hooks/use-empty-values/index.mjs';
export { ariaProps, useAriaProps } from './hooks/use-aria/index.mjs';const install = installer.install;
const version = installer.version;export { install, version };
//# sourceMappingURL=index.mjs.map
注意,这个index.mjs并不是打包后文件。因此这时引用混用是没有问题的。
import {ElDatePicker} from 'element-plus';
import {ElDatePicker} from 'element-plus/components/date-picker';
组件打包后,前端如何使用?关键是要能把一个个文件代码再拆开,否则无法兼容分散引用。因此,不能用webpack的模式打包,因为无法拆开。只能自己建立一个打包格式,如:
{modules:[{path:'/npm/element-plus/es/components/date-picker',code:......},{path:'/npm/element-plus/es/components/card',code:......},]
}
浏览器前端拿到这个打包结果后,需要一一拆开。在es5模式下,因为require函数是你自己实现的,你的require函数可以支持内联加载(inline module import),即直接加载code而不是url,不赘述。
但是es6下,import和import()是浏览器内置的,你无法修改。特别的,nodejs的import有实现这种模式:Modules: ECMAScript modules | Node.js v24.0.2 Documentation
那如何办呢?
(1)等待新的es标准,各大浏览器支持内联加载(inline module import)
(2)在service worker中,拦截fetch
本文主要讲service worker中如何处理打包拆包。
拆包后代码可以放在浏览器cache中,也可以放在内存中。放在cache中比较慢,好处是浏览器关闭后,下次打开可以直接用。
如:拆到内存中:
/*** 把小模组存储到内存中*/
const memoryCache={};
const cacheHeaders={};
let foil={onActivate(){return clients.claim();},getModule(req,cb){let u=new URL(req.url);let url=u.pathname+u.search;// console.log(prefix,url);let m=memoryCache[url];let res;if (m){res=new Response(m.code,{status:m.status,headers:{'Content-Type': 'application/javascript','Location':m.location,}});} cb(null,res);},async parseBundleResult(bundleObj,cacheHeaders){//console.log(prefix,''bundle result:',bundleObj);for(let i=0;i<bundleObj.modules.length;i++){let m=bundleObj.modules[i];//console.log(prefix,m.path,m.location);/*** 作业打包时,可能只返回了依赖列表而没有代码,没有代码就不加入cache中。*/if (!m.code) continue;if (m.location){let originDir=m.path.slice(0, m.path.lastIndexOf("/") + 1);let realDir=m.location.slice(0, m.location.lastIndexOf("/") + 1);if (originDir!=realDir){/*** 重定向的路径变了,会影响相对路径的import/require加载,* 增加一个单独的路由。*/memoryCache[m.location]={status:200,code:m.code};memoryCache[m.path]={status:301,location:m.location};}else memoryCache[m.path]={status:200,code:m.code};}else memoryCache[m.path]={status:200,code:m.code};cacheHeaders[m.path]=m.cacheHeaders['last-modified'];if (m.location) cacheHeaders[m.location]=m.cacheHeaders['last-modified'];}},getCacheHeaders(){return cacheHeaders;}
}
如:拆到cache中:
/*** 把小模组存储到浏览器缓存中*/
const CACHE_NAME = "foil-spa-vue-v1";
const key_cacheHeaders='/_cache_meta_info';function module2Response(m){let res1,res2;let status=200;let code=m.code;let headers={'Content-Type': 'application/javascript','Content-Length':new Blob([m.code]).size.toString(),}if (m.location){//console.log(prefix,m.path,m.location);let originDir=m.path.slice(0, m.path.lastIndexOf("/") + 1);let realDir=m.location.slice(0, m.location.lastIndexOf("/") + 1);if (originDir!=realDir){/*** 重定向的路径变了,会影响相对路径的import/require加载,* 增加一个单独的路由。*/res2={path:m.location,res:new Response(m.code,{status:200,headers})};status=301;code=undefined;headers={'Content-Type': 'application/javascript','Location':m.location,'Content-Length':0}}}res1={path:m.path,res:new Response(code,{status,headers})};return [res1,res2];
}let foil={onActivate(){let t1=new Date().getTime();let task=new Promise(async function(resolve,reject){try{let cacheNames=await caches.keys();await cacheNames.filter(function (cacheName) {// 过滤掉不需要的缓存return (cacheName.startsWith("foil-spa-vue-") && cacheName !== CACHE_NAME);}).map(function (cacheName) {// 删除旧的缓存return caches.delete(cacheName);});// 立即接管所有客户端await clients.claim();console.log(prefix,"activate clear cache,",new Date().getTime()-t1,'ms');resolve();}catch(err){reject(err);}});return task;},getModule(req,cb){caches.match(req).then(function (res) {cb(null,res);return res;},cb);},async parseBundleResult(bundleObj,cacheHeaders){let cache=await caches.open(CACHE_NAME);let tasks=[];for(let i=0;i<bundleObj.modules.length;i++){let m=bundleObj.modules[i];if (!m.code) continue;let [res1,res2]=module2Response(m);tasks.push(cache.put(res1.path,res1.res));if (res2) tasks.push(cache.put(res2.path,res2.res));cacheHeaders[m.path]=m.cacheHeaders['last-modified'];if (m.location) cacheHeaders[m.location]=m.cacheHeaders['last-modified'];}let cacheHeadersJSON=JSON.stringify(cacheHeaders);tasks.push(cache.put(key_cacheHeaders,new Response(cacheHeadersJSON,{headers:{'Content-Length':new Blob([cacheHeadersJSON]).size.toString()}})));await Promise.all(tasks);},async getCacheHeaders(){let cacheHeaders={};let cache=await caches.open(CACHE_NAME);let cacheRes=await cache.match(key_cacheHeaders);if (cacheRes){cacheHeaders=await cacheRes.json();}return cacheHeaders;}
}
在service worker中拦截fetch调用,注意要处理post的body。
self.addEventListener("fetch", function (event) {//console.log(prefix,'fetch',event.request.url);let task = new Promise(function (resolve, reject) {foil.getModule(event.request, function (err, res) {if (err) {reject(err);} else if (res) resolve(res);else {/*** 看到event.request.destination是'script',但是后台收到的req.headers['sec-fetch-dest']是'empty'* 这导致es6下,后台对/npm/react/umd/react.development.js这个es5文件不能转码。* 增加'sec-fetch-dest'无效,只能增加'tm-sec-fetch-dest'。* es6下为了处理import ".";这种非法语句,需要把原始referer传递到后台。* 注意:req.referrer单词与referer不同,多一个r字母。** Request 对象的 headers 是只读的,没办法直接进行修改。当你使用 req.headers.set() 时,浏览器会报错,提示 Headers are immutable(头部不可变)。*/let req = event.request;let bodyPromise;if (req.method === "POST" || req.method === "PUT") {const contentType = req.headers.get("Content-Type");if (contentType && contentType.includes("application/json")) {bodyPromise = req.json().then(function(json){return JSON.stringify(json);});} else if (contentType && contentType.includes("form")) {bodyPromise = req.formData();} else {bodyPromise = req.text();}} else {bodyPromise = Promise.resolve(null);}bodyPromise.then(function (body) {const newHeaders = {"tm-referer": req.referrer,"tm-sec-fetch-dest": req.destination,};for (let [key, value] of req.headers.entries()) {newHeaders[key] = value;}let mode = req.mode;if (mode === "navigate") {/*** 修正 mode 属性,避免使用 'navigate'* 对于导航请求,降级为 cors 或 same-origin* 否则报错:Uncaught (in promise) TypeError: Failed to construct 'Request': Cannot construct a Request with a RequestInit whose mode member is set as 'navigate'.*/mode = req.url.startsWith(self.origin) ? "same-origin" : "cors";}req = new Request(req, {method: req.method,headers: newHeaders,body: body,mode: mode,credentials: req.credentials,cache: req.cache,redirect: req.redirect,referrer: req.referrer,});res = fetch(req).then(function (data) {return data;},function (err) {/**增加then后,不在浏览器显示重复的错误信息*/});resolve(res);return body;}, reject);}});});event.respondWith(task);
});
注意:
(1)每次后台打包,可能要花2-3秒,最好把打包结果保存到文件,下次直接读取。或者服务器启动时,预热打包一次。
(2)前端如何更新包内个别文件。前端记录每个文件的last-modified,打包时全部传到后台,文件多时可能有几十K。
(3)service worker的使用限制,必须是https有合法ca证书,且不能是https://localhost。
如此,SPA宿主页加载速度可以提高40-50%。
相关文章:
SPA模式下的es6如何加快宿主页的显示速度
SPA的模式下,宿主页是首先加载的页面,会需要一些主要的组件,如element-plus,easyui,devextreme,ant-design等,这些组件及其依赖组件,文件多,代码量大,可能导致…...
windows powershell 判断 进程号是否存在
在 Windows PowerShell 中,你可以使用多种方法来检查一个特定的进程号(PID)是否存在。以下是几种常用的方法: 方法1:使用 Get-Process 命令 你可以尝试获取具有特定 PID 的进程。如果该进程存在,Get-Proce…...
c# 解码 encodeURIComponent
在C#中,如果你需要解码由encodeURIComponent方法编码的URL,你可以使用System.Web命名空间中的HttpUtility.UrlDecode方法。这个方法可以处理由JavaScript的encodeURIComponent方法编码的字符串。 首先,确保你的项目中引用了System.Web命名空…...

Spring AI:Java开发者的AI开发新利器
目录 一、引言 二、Spring AI 是什么 三、核心功能与特性 3.1 统一的 API 抽象 3.2 丰富的模型支持 3.3 低代码集成 3.4 结构化数据输出 3.5 流式数据响应 四、应用场景 4.1 智能客服系统 4.2 图像识别应用 4.3 数据分析与预测 五、快速上手 5.1 环境搭建 5.2 创…...
Android System UI 深度解析:从架构演进到车载 / TV 场景的全维度定制
Android System UI 是 Android 操作系统的核心组件,负责管理设备的系统级用户界面和交互逻辑。它通过状态栏、导航栏、通知面板、快速设置等功能,为用户提供与系统功能直接交互的入口,并与硬件、应用程序深度协同,构建完整的用户体验。以下是其核心架构、功能演进及定制化能…...

Spring Cloud Sleuth与Zipkin深度整合指南:微服务链路追踪实战
上篇文章简单介绍了SpringCloud系列熔断器:Sentinel的搭建及基本用法,今天继续讲解下SpringCloud的微服务链路追踪:Zipkin的使用!在分享之前继续回顾下本次SpringCloud的专题要讲的内容: 前置知识说明 在开始本教程前…...
React从基础入门到高级实战:React 基础入门 - 列表渲染与条件渲染
列表渲染与条件渲染 在 React 开发中,列表渲染 和 条件渲染 是处理动态数据和用户交互的基础技术。通过列表渲染,你可以根据数据动态生成 UI 元素;而条件渲染则让你根据特定条件展示不同的内容。这两个技能在实际项目中非常常见,…...
在 stm32 中 volatile unsigned signed 分别有什么作用,分别在什么场景下使用?
在STM32开发中, plaintext 复制 volatile 、 plaintext 复制 unsigned 和 plaintext 复制 signed 是三个关键的关键字,它们的用途和场景如下: 1. plaintext 复制 volatile 关键字 作用: 禁止编译器优化ÿ…...
FreeBSD14.2因为爆内存而导致Xfce4视窗被卡,桌面变黑色,只能看到鼠标在窗体中心,鼠标无反应,键盘无反应
拿问题问AI pytest的时候因为内存不足导致xfce4卡住(xfce4相关进程被关闭),桌面变黑色,只能看到鼠标在窗体中心,鼠标无反应,键盘无反应,请问应该怎么办? pytest的时候因为内存不足导…...
k8s-NetworkPolicy
在 Kubernetes 中,NetworkPolicy 是一种资源对象,用于定义 Pod 之间的网络通信策略。它允许你控制哪些 Pod 可以相互通信,以及如何通信。通过使用 NetworkPolicy,可以实现更细粒度的网络访问控制,增强集群的安全性。 1…...

spring-ai 集成 mcp 之投机取巧
主旨 这篇文章主旨就一点,罗列spring-ai对mcp集成导致出现的一系列问题 分析 由于mcp未问世之前,就早就已经有了工具调用,源码如下: public interface ToolCallback {/*** Definition used by the AI model to determine when a…...

大语言模型的完整训练周期从0到1的体系化拆解
以下部分内容参考了AI。 要真正理解大语言模型(LLM)的创生过程,我们需要将其拆解为一个完整的生命周期,每个阶段的关键技术相互关联,共同支撑最终模型的涌现能力。以下是体系化的训练流程框架: 阶段一&am…...

历年北京邮电大学保研上机真题
2025北京邮电大学保研上机真题 2024北京邮电大学保研上机真题 2023北京邮电大学保研上机真题 在线测评链接:https://pgcode.cn/problem?classification1 32位二进制串加法 题目描述 输入一个32位的二进制01串,输出这个数1和3后的32位二进制串。 输入…...
elementUI 中el-date-picker和el-select的样式调整
1. el-date-picker <el-date-picker class"select1" size"small" v-model"timeRangeArr" type"daterange" align"right" unlink-panels range-separator"至" start-placeholder"开始日期" end-pla…...

《仿盒马》app开发技术分享-- 定位获取(端云一体)
开发准备 上一节我们实现了地址管理页面的数据查询和展示,接下来我们要实现的功能是地址添加相关的,我们想实现的功能是地图选点,那么在地图选点之前我们要做的就是先获取用户当前的定位。获取定位后我们拿到经纬度和其他信息,然…...

黑马点评--基于Redis实现共享session登录
集群的session共享问题分析 session共享问题:多台Tomcat无法共享session存储空间,当请求切换到不同Tomcat服务时,原来存储在一台Tomcat服务中的数据,在其他Tomcat中是看不到的,这就导致了导致数据丢失的问题。 虽然系…...

Mujoco 学习系列(二)基础功能与xml使用
这篇文章是 Mujoco 学习系列第二篇,主要介绍一些基础功能与 xmI 使用,重点在于如何编写与读懂 xml 文件。 运行这篇博客前请先确保正确安装 Mujoco 并通过了基本功能与GUI的验证,即至少完整下面这个博客的 第二章节 内容: Mujoc…...

比特授权云外壳加密支持Android 15!
在信息化时代,多数软件供应商需要适配安卓系统,以扩大市场、满足用户需求并提升竞争力。APK作为Android应用的安装包,包含代码、资源、配置文件等运行所需组件,用于在设备端分发和安装应用。企业在分发软件时,需要通过…...

uniapp使用sse连接后端,接收后端推过来的消息(app不支持!!)
小白终成大白 文章目录 小白终成大白前言一、什么是SSE呢?和websocket的异同点有什么?相同点不同点 二、直接上实现代码总结 前言 一般的请求就是前端发 后端回复 你一下我一下 如果需要有什么实时性的 后端可以主动告诉前端的技术 我首先会想到 webso…...

历年复旦大学保研上机真题
2025复旦大学保研上机真题 2024复旦大学保研上机真题 2023复旦大学保研上机真题 在线测评链接:https://pgcode.cn/problem?classification1 最大公共子串 题目描述 输入 3 个子串,输出这 3 个子串的最大公共子串。 输入格式 输入包含 3 个子串&…...

黑马点评-实现安全秒杀优惠券(使并发一人一单,防止并发超卖)
一.实现优惠券秒杀 1.最原始代码: Service public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {Resourceprivate ISeckillVoucherService seckillVoucherService;Resourcepriv…...

解决论文中字体未嵌入的问题
文章总览:YuanDaiMa2048博客文章总览 解决论文中字体未嵌入的问题 问题描述解决方案:使用 Adobe PDF 打印机嵌入字体(WPS版)步骤一:打开 PDF 文件步骤二:选择打印到 Adobe PDF步骤三:修改 Adobe…...

leetcode 131. Palindrome Partitioning
目录 一、题目描述 二、方法1、回溯法每次暴力判断回文子串 三、方法2、动态规划回溯法 一、题目描述 分割回文子串 131. Palindrome Partitioning 二、方法1、回溯法每次暴力判断回文子串 class Solution {vector<vector<string>> res;vector<string>…...
Android本地语音识别引擎深度对比与集成指南:Vosk vs SherpaOnnx
技术选型对比矩阵 对比维度VoskSherpaOnnx核心架构基于Kaldi二次开发ONNX Runtime + K2新一代架构模型格式专用格式(需专用工具转换)ONNX标准格式(跨框架通用)中文识别精度89.2% (TDNN模型)92.7% (Zipformer流式模型)内存占用60-150MB30-80MB迟表现320-500ms180-300ms多线程…...

审计报告附注救星!实现Word表格纵向求和+横向计算及其对应的智能校验
在审计工作中,Word附注通常包含很多表格。为了确保附注数字的准确性,我们需要对这些表格进行数字逻辑校验,主要包含两个维度:在纵向上验证合计项金额是否正确;在横向上检查“年末金额年初金额本期增加-本期减少”的勾稽…...

人工智能数学基础实验(四):最大似然估计的-AI 模型训练与参数优化
一、实验目的 理解最大似然估计(MLE)原理:掌握通过最大化数据出现概率估计模型参数的核心思想。实现 MLE 与 AI 模型结合:使用 MLE 手动估计朴素贝叶斯模型参数,并与 Scikit-learn 内置模型对比,深入理解参…...

告别延迟!Ethernetip转modbustcp网关在熔炼车间监控的极速时代
熔炼车间热火朝天,巨大的热风炉发出隆隆的轰鸣声,我作为一名技术操控工,正密切关注着监控系统上跳动的各项参数。这套基于EtherNET/ip的监控系统,是我们车间数字化改造的核心,它将原本分散的控制单元整合在一起&#x…...
Kotlin协程优化Android ANR问题
引言 在Android开发中,ANR(Application Not Responding)是用户体验的致命杀手。当主线程被耗时操作阻塞超过阈值(5秒前台/10秒后台),系统会直接弹窗提示应用无响应。本文将深入剖析如何通过Kotlin协程将耗…...

Visual Studio Code插件离线安装指南:从市场获取并手动部署
Visual Studio Code插件离线安装指南:从市场获取并手动部署 一、场景背景二、操作步骤详解步骤1:访问官方插件市场步骤2:定位目标版本步骤3:提取关键参数步骤4:构造下载链接步骤5:下载与安装 三、注意事项 …...
构建安全AI风险识别大模型:CoT、训练集与Agent vs. Fine-Tuning对比
构建安全AI风险识别大模型:CoT、训练集与Agent vs. Fine-Tuning对比 安全AI风险识别大模型旨在通过自然语言处理(NLP)技术,检测和分析潜在的安全威胁,如数据泄露、合规违规或恶意行为。本文从Chain-of-Thought (CoT)设计、训练集构建、以及Agent-based方法与**AI直接调优…...