3

模块联邦

解决微前端架构模块共享问题

SystemJS,webpack5

systemJs模块加载器,惰性,

  1. 加载模块:SystemJS模块或者UMD模块
  2. 依赖关系:运行时确定,importMap
  3. 支持webpack4/5

webpack5模块联邦

  1. 对模块没有限制
  2. 依赖关系:编译时确定,remoteEntry
  3. 仅支持webpack5

    //basecore   
    //remotes部分直接引入无法加载,原因不明确
    const remoteComponents = require('./plugins/remote-components.js')
    module.exports = {
      publicPath:'auto',
      outputDir,
      lintOnSave: true,
      devServer: {
        // publicPath, // 和 publicPath 保持一致
        port, // 端口
        hot:true,
        allowedHosts: 'auto',// 关闭 host check,方便使用 ngrok 之类的内网转发工具
        headers: {
          'Access-Control-Allow-Origin': '*'
        },
        
        historyApiFallback: {
          // 将以 xxx 开头的请求重定向到根路径
          rewrites: [{ from: new RegExp(`^\/child-core`), to: '/' }],
        },
        client: {
          overlay: false
        },
        
        
      },
    configureWebpack: config => {
        const configNew = {
          plugins: [],
          resolve: {
            fallback: {
              util: require.resolve('util/'),
              assert: require.resolve('assert/'),
              timers: require.resolve('timers-browserify')
            }
          }
        } 
        configNew.plugins.push(
              new webpack.container.ModuleFederationPlugin({
                name: 'baseCore',
                library: { type: "umd", name: "baseCore" },
                filename: 'remoteEntry.js',
                shared: remoteComponents.shared,
                exposes: remoteComponents.exposes,
                remotes:{
                  childApp:
                  `promise new Promise(resolve => {
                  const timeStamp = Date.now();
                  const remoteUrlWithTimeStamp = '${process.env.VUE_APP_CHILD_CORE_URL}remoteEntry.js?time=' + timeStamp;
                  const script = document.createElement('script')
                  script.src = remoteUrlWithTimeStamp
                  script.onload = () => {
                    // the injected script has loaded and is available on window
                    // we can now resolve this Promise
                    const proxy = {
                      get: (request) => window.childApp.get(request),
                      init: (arg) => {
                        try {
                          return window.childApp.init(arg)
                        } catch(e) {
                          console.log('remote container already initialized')
                        }
                      }
                    }
                    resolve(proxy)
                  }
                  // inject this script with the src set to the versioned remoteEntry.js
                    document.head.appendChild(script);
                  })
                  ` 
                },
                // shared: remoteComponents.shared,
    
              })
            )
            configNew.plugins.push(new SpeedMeasurePlugin())
            return configNew
      },
    }
    
    
    //childCore
    //该部分无法引入base 无法双向 原因不知道
    configNew.plugins.push(
          new webpack.container.ModuleFederationPlugin({
            name: 'childApp',
            library: { type: "umd", name: "childApp" },
            remotes:{
            },
            filename: 'remoteEntry.js',
            exposes: remoteComponents.exposes,
            shared: remoteComponents.shared,
          })
        )

webpack module federation

/**
ContainerPlugin,将exposes的模块,当成入口文件模块打包,形成单独可运行的文件,随主模块编译
**/
if(options.exposes...)new ContainerPlugin
/**
ContainerRefrencePlugin,多个不同container调用关系判断,实现模块通信和传递
**/
if(options.remotes)new ContainerRefrencePlugin
/**
**/
if(options.shared) new SharePlugin

micro-app与模块联邦结合

  1. 明确角色,micro-app作为基座应用负责加载和管理各类项目,模块联邦用于代码共享
  2. 配置模块联邦,项目中配置模块联邦
  3. 在作为基座的项目中集成micro-app
  4. 跨域
  5. 状态和上下文共享,本地存储,micro-app数据通信,模块联邦共享全局状态
//配置基座
//加载方式确保micro-app在项目加载初期加载完毕
//micro-app.js
import microApp from '@micro-zoe/micro-app'
microApp.start({
  tagName: 'micro-app-core',
  lifeCycles: {
      mounted() {
          // 设置全局数据
          if (window.rawWindow) {
              window.rawWindow.__CORE_CONFIG__ = window.__IBPS_CONFIG__
          }
      }
  }
})
//bootstrap.js
import('./main')
//index.js
import('./bootstrap');
// micro-app不能异步加载,否则无法成功渲染
import "../plugins/micro-app";
//入口
 index: {
    entry: 'src/index.js',
    template: 'public/index.html',
    filename: 'index.html',
    chunks: [...]}

为什么需要micro-app+webpack5模块联邦

  1. micro-app可以在spa中整合不同技术栈和框架的应用,可以作为容器进行加载和隔离,模块联邦可以实现应用间的共享
  2. micro-app运行每个应用独立部署更新,模块联邦允许共享依赖组件

为什么可以分开使用

都是微前端解决方案,如果不需要集成多个项目仅使用模块联邦可以满足

webpack

打包顺序

  1. webpack配置文件(webpack.config.js/vue.config.js)
  2. 输入执行 build
  3. options:解析shell和config.js配置项,激活webpack加载项和插件
  4. webpack(options):创建Compiler对象,初始化基础插件
  5. compiler:4-->5生成的compiler,全程唯一,启动webpack时一次性创建,完整的webpack配置:options,loader,plugins,以该对象访问webpack主环境
  6. compiler.run()
  7. compliation:6-->7生成的Compliation对象,检测到文件变化,都会生成新的compliation对象,包括当前模块资源,编译生成资源,变化文件,被追踪的依赖状态信息,属性:entries,modules(所有模块),chunks(代码块),assets,template
  8. compiler.make():生成compliation后执行
  9. compliation.addEntry()
  10. compliation.buildModule()
  11. ast分析,递归依赖
  12. compilation.seal():整理chunk,module,每个chunk对应一个入口文件
  13. complier.emitAssets()
[^无新的compliation生成时]: [1-5]-->6-->8--->13



[^10-11]: 调用loader处理源文件,使用acorn生成ast,遇require,push到数组,处理module后,开始异步递归处理依赖module

性能优化

  1. 入口起点优化,配置多入口
  2. 动态导入:es6 import()
  3. split chunk

    config.optimization.splitChunks({
         cacheGroups: {
           // 所有页面共有的外部依赖关系
           libs: {
             name: 'chunk-vendor',
             chunks: 'initial',
             minChunks: 1,
             test: /[\\/]node_modules[\\/]/,
             priority: 1,
             reuseExistingChunk: true,
             enforce: true
           },
           // 对所有页面通用的代码
           common: {
             name: 'chunk-common',
             chunks: 'initial',
             minChunks: 2,
             maxInitialRequests: 5,
             minSize: 0,
             priority: 2,
             reuseExistingChunk: true,
             enforce: true
           },
       
           // 仅供首页使用的外部依赖项
           index: {
             name: 'chunk-index',
             chunks: 'all',
             minChunks: 1,
             test: /[\\/]node_modules[\\/](sortablejs|screenfull|nprogress|hotkeys-js|fuse\.js|better-scroll|lowdb|xlsx|axios)[\\/]/,
             priority: 3,
             reuseExistingChunk: true,
             enforce: true
           },
           // Vue 全家桶
           vue: {
             name: 'chunk-vue',
             test: /[\\/]node_modules[\\/](vue|vue-router|vuex)[\\/]/,
             chunks: 'all',
             priority: 3,
             reuseExistingChunk: true,
             enforce: true
           },
           // element-ui
           element: {
             name: 'chunk-element',
             test: /[\\/]node_modules[\\/]element-ui[\\/]/,
             chunks: 'all',
             priority: 3,
             reuseExistingChunk: true,
             enforce: true
           },
           vxe: {
             name: 'chunk-vxe',
             test: /[\\/]node_modules[\\/]vxe-table[\\/]/,
             chunks: 'all',
             priority: 3,
             reuseExistingChunk: true,
             enforce: true
           },
           dynamic:{
             name :'chunk-dynamic',
             test:/[\\/]business[\\/]platform[\\/]/,
             chunks: 'all',
             minChunks: 5,
             priority: 3,
             reuseExistingChunk:true,
             maxSize: 102400
           },
           dynamicform:{
             name :'chunk-dynamicform',
             test:/[\\/]business[\\/]platform[\\/]form[\\/]/,
             chunks: 'all',
             minChunks: 5,
             reuseExistingChunk:true,
             priority: 4,
           }
         }

4.cdn

let cdn = {}
if (enableCDN) {
  cdn = {
  // 以CDN链接的形式引入与index相关的外部依赖
    index: cdnDependencies,
    // 以CDN链接的形式引入与移动页面相关的外部依赖
    subpage: []
  }
}

// 设置不参与构建的外部依赖包
const externals = {}
keys(pages).forEach(name => {
  if (cdn[name]) {
    cdn[name].forEach(p => {
      if (p.library !== '') {
        externals[p.name] = p.library
      }
    })
  }
})
configureWebpack: config => {
    const configNew = {
      plugins: []
    }
    if (NODE_ENV === 'production') {
      configNew.externals = externals
    }
}
chainWebpack: config => { 
    keys(pages).forEach(name => {
        const packages = cdn[name]
        config.plugin(`html-${name}`).tap(options => {
            const setting = {
                css: compact(map(packages, 'css')),
                js: compact(map(packages, 'js'))
            }
            set(options, '[0].cdn', NODE_ENV === 'production' ? setting : [])
            return options
        })
    })
}
//index.html
 <!-- 使用 CDN 加速的 CSS 文件,配置在 vue.config.js 下 -->
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
  <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style">
        <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet">
            <% } %>
 <!-- 使用 CDN 加速的 JS 文件,配置在 vue.config.js 下 -->
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
                    <link href="<%= htmlWebpackPlugin.options.cdn.js[i] %>" rel="preload" as="script">
                        <% } %>

5.teser.cssminimizer,js css代码压缩,

​ 5.1 提取css mini-css-extract-plugin

​ 5.2 压缩css optimize-cssnano-plugin

​ 5.3 vue中默认 TerserPlugin 压缩js

module.exports={
    configureWebpack:config =>{
        return {
            optimization:{
                minimize: true
            }
        }
    }
}

6.treeShaking ==> usedExporte = true

7.gzip

  const ignorePlugins =new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
        const productionGzipExtensions = ['js', 'css']
        const gzipPlugins = new CompressionWebpackPlugin({
          filename: '[path][base].gz[query]',
          test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
          threshold: 10240,
          minRatio: 0.8,
          deleteOriginalAssets: false
        })
        configNew.plugins.push(gzipPlugins)
        configNew.plugins.push(ignorePlugins)

8.DllPlugin和DllReferencePlugin

//dll.config.js
const path = require('path')
const webpack = require('webpack')
const package = require('../package.json')
const AssetsPlugin = require('assets-webpack-plugin')
//读取package.json里的依赖,normalize.css除外,打包会报错
const package = require('../package.json')
let dependencies = Object.keys(package.dependencies) || []
//如果使用了chrome的vue-devtool,那打包的时候把vue也排除掉,因为压缩过的vue是不能使用vue-devtool的
dependencies = dependencies.length > 0 ? dependencies.filter(item => item !== 'vue') : []

module.exports = {
  entry: {
    vendor: dependencies
  },
  output: {
    path: path.join(__dirname, '../static'),
    filename: 'dll.[name]_[hash:6].js',
    library: '[name]_[hash:6]'
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.join(__dirname, '../', '[name]-manifest.json'),
      name: '[name]_[hash:6]'
    }),
    new AssetsPlugin({
      filename: 'bundle-config.json',
      path: './'
    })
  ]
}
webpack -p --progress --config build/webpack.dll.conf.js
//webpack.config.js
const manifest = require('../vendor-manifest.json')
...
plugins: [
    new webpack.DllReferencePlugin({
      manifest
    })
  ]
//index.html
<script src="./static/dll.vendor.js"></script>

8.缓存 优化构建速度 HardSourceWebpackPlugin

 if (enableCache) {
      const cachePlugins = [
        new HardSourceWebpackPlugin(),
        new HardSourceWebpackPlugin.ExcludeModulePlugin([
          {
            test: /mini-css-extract-plugin[\\/]dist[\\/]loader/
          }
        ])
      ]
      configNew.plugins.push(...cachePlugins)
    }

9.去除console

config.when(NODE_ENV === 'production' ,config=>{
      config.optimization.minimizer('terser').tap((args) => {
        args[0].parallel = 4
        args[0].terserOptions.compress.warnings = true
        args[0].terserOptions.compress.drop_console = true
        args[0].terserOptions.compress.pure_funcs = ['console.log']
        return args
      })
    })

10.image-webpack-loader 压缩图片

11.按需加载

12.多线程压缩js terser-webpacl-plugin

性能分析工具

  1. speed-measure-webpack-plugin:分析loader和plugin执行时间

    const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
    module.exports = {
      chainWebpack: config => {
        config
          .plugin('speed-measure-webpack-plugin')
          .use(SpeedMeasurePlugin)
          .end()
      }
    }
  1. webpack-bundle-analyzer:分包情况
  2. 查看 vuecli 配置

    vue inspect --mode production > output.js

webpack4-->webpack5

  1. 不需要HardSourceWebpackPlugin,内置filesystem,不在自动注入node polyfill核心模块
  2. IgnorePlugin变化
  3. 移除file-loader和url-loader
  4. thread-loader 替换happyPack
  5. allowedHosts 代替 disableHostCheck
  6. inline移除
  7. contenBase 变为static对象配置
  8. module.exports = {
    
     publicPath:'auto',
    
     outputDir,
    
     lintOnSave: true,
    
     devServer: {
    
      // publicPath, // 和 publicPath 保持一致
    
      port, // 端口
    
      hot:true,
      configureWebpack:config=>{
            resolve: {
            fallback: {
                //人工注入核心模块
              util: require.resolve('util/'),
              assert: require.resolve('assert/'),
              timers: require.resolve('timers-browserify')
            }
          }
            configNew.cache = {
             type: 'filesystem',
            allowCollectingMemory: true
          }
          //old
    //const ignorePlugins =new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
          //new
         const ignorePlugins =new webpack.IgnorePlugin({
              resourceRegExp:/^\.\/locale$/, 
              contextRegExp:/moment$/})
          
      }
    
  9.     "@babel/eslint-parser": "^7.23.10",
        "@kazupon/vue-i18n-loader": "^0.5.0",
        "@vue/cli-plugin-babel": "^5.0.8",
        "@vue/cli-plugin-router": "^5.0.8",
        "@vue/cli-plugin-unit-jest": "^5.0.8",
        "@vue/cli-plugin-vuex": "^5.0.8",
        "@vue/cli-service": "^5.0.8",
        "@vue/eslint-config-standard": "^6.1.0",
        "@vue/test-utils": "^1.3.0",
        "babel-eslint": "^10.0.3",
        "babel-plugin-dynamic-import-node": "^2.3.3",
        "circular-dependency-plugin": "^5.2.2",
        "compression-webpack-plugin": "^6.1.1",
        "core-js": "^3.19.1",
        "eslint": "^7.32.0",
        "eslint-plugin-import": "^2.25.3",
        "eslint-plugin-node": "^11.1.0",
        "eslint-plugin-promise": "^5.1.1",
        "eslint-plugin-vue": "^7.20.0",
        
        "html-webpack-plugin": "^5.6.0",
        webpack@5.89.0 
    

内存泄漏分析

vue2 keepAlive

版本:vue 2.6.14

问题:关闭某个页面 内存文档泄漏 x mb

原因:https://github.com/vuejs/vue/issues/9842

解决方案:vue 打补丁 patch-package

问题:仍存在内存泄漏。

原因:页面销毁后,功能正常,但dom元素挂载到了最外层上,具体原因未知,疑渲染混乱问题,

解决:手动清除keepalive内存

let vm = null
// 删除非当前页面 释放keepAlive内存
export function handleRouteClean(delPage){
  let fullPath = delPage.fullPath
  let Vnode = vm.$children
  let vnode
  for(let item of Vnode){
    if (item.$vnode.data.key && item.$vnode.data.key==fullPath) {
      vnode = item.$vnode;
      break;
    }
  }
  if(vnode){
    delKeepAliveCache(vnode)
  }
  
  Vnode = null
}
function delKeepAliveCache(vnode){
  let key = vnode.key || vnode.componentOptions.Ctor.cid + (vnode.componentOptions.tag ? `::${vnode.componentOptions.tag}` : "")
 
  let parent = vnode.parent.componentInstance
  let cache = parent.cache
  let keys = parent.keys
  let vnodes
  if(cache[key]){
    cache[key].componentInstance.$destroy();
    if(keys.length){
      let index = keys.indexOf(key)
      if(index > -1){
        keys.splice(index, 1)
      }
    }
    if (vnode.children || vnode.componentInstance?._vnode?.children) {
      vnodes = vnode.children || vnode.componentInstance._vnode.children
      for (const vn of vnodes) {
        destroyDeep(vn)
      }
    }
    vnode.elm.innerHTML = ''
    vnode.componentInstance = null
    vnodes = null
    delete cache[key]
  }
}
function destroyDeep(vnode){
  let vnodes
  if (vnode.children || vnode.componentInstance?._vnode?.children) {
    vnodes = vnode.children || vnode.componentInstance._vnode.children
    for (const vn of vnodes) {
      destroyDeep(vn)
    }
  }
    vnode.elm.innerHTML = ''
    vnode.elm.innerText = ''
    vnode.componentInstance?.$destroy();
    vnode.componentInstance = null
    vnodes = null 
  
}
// 删除当前页面 释放keepAlive内存
export function destroyKp(delPage){
  let Vnode = vm.$children
  let fullPath = delPage.fullPath
  let reg = new RegExp(fullPath)
  for(let item of Vnode){
    let parent = item.$vnode.parent.componentInstance
   
    let cache = parent.cache
    let keys = parent.keys
    let key 
    for(let cacheKey of keys){
      if(reg.test(cacheKey)){
        key = cacheKey
        break
      }
    }
    if(key && cache[key]){
      if(cache[key].componentInstance.children || cache[key].componentInstance._vnode.children){
        let vnodes = cache[key].componentInstance.children || cache[key].componentInstance._vnode.children
        for (const vn of vnodes) {
          destroyDeep(vn)
        }
      }
      cache[key].componentInstance.$destroy();
      if(keys.length){
        let index = keys.indexOf(key)
        if(index > -1){
          keys.splice(index, 1)
        }
      }
      delete cache[key]
      break
    }
  }
 
}
export function receive(that){
  vm = that
}

问题:手动清楚后,仍存在对销毁页面的引用

原因:routes中 存在 instances引用

解决:条件记录删除页面,手动获取routes中的instance

function removeRouter(pages){
  const routes = router.getRoutes()
  let nameMap = new Map()
  for (let index = 0; index < routes.length; index++) {
    const r = routes[index];
    nameMap.set(r.path, r)
  }
  for(let i = 0 ;i<pages.length;i++){
    const rPage = nameMap.get(pages[i])
    if (rPage && util.isNotEmpty(rPage.instances)) rPage.instances.default = undefined
  } 
  nameMap = null
  store.dispatch('ibps/page/changePage')
}

debounce 造成的内存泄漏

//内存始终存在,未调用 lodash 销毁
debounce(() => {
      that.handleTableHeight(true, true, true)
    }, 100)
this.debounceListner =  debounce(() => {
      that.handleTableHeight(true, true, true)
    }, 100)
this.debounceListner.cancel()
this.debounceListner = null

window.requestAnimationFrame 造成的内存泄漏

window.cancelAnimationFrame(this.rafId)

elementui源码的内存泄漏

问题 :Popper,buttom 组件销毁持续引用销毁页面

原因:源码中存在引用,未完全销毁

解决:1,查看git 是否修复版本 ,若修复 升级

2 ,patch-package,issue中有解决方案 解绑源码监听事件

监听事件,定时器

解绑,记录id清除

远程请求封装js内存泄漏

问题: Map 对象未清理,remoteFunc,callback数组存在潜在内存泄漏=》回调用不执行

import { REMOTE_REQUEST_TIMEOUT, REMOTE_TRANS_REQUEST_TIMEOUT } from '@/constant'

const cache = new Map()

const cacheTime = new Map()
/**
 * 远程获取[避免重复请求]
 * @param prefix 前缀
 * @param params 参数配置
 * @param remoteFunc 反馈参数
 * @param repeatRequest 是否重复请求
 * @returns {Promise<any>|Promise<T | never>}
 */
export function remoteRequest(prefix, params, remoteFunc, repeatRequest = true, timeout) {
  if (params == null) {
    return new Promise((resolve) => {
      resolve([])
    })
  }
  if (!timeout) {
    timeout = REMOTE_REQUEST_TIMEOUT
  }
  timeout=5000
  const timeKey = prefix + '#' + JSON.stringify(params)
  let time = ''
  if (repeatRequest) {
    const curTime = new Date().getTime()
    time = cacheTime.get(timeKey)
    if (time == null) {
      cacheTime.set(timeKey, curTime)
      time = curTime
    }

    if (curTime - timeout >= time) { // 在指定时间内的请求都重新请求
      // 清除缓存换个请求
      cacheTime.clear()
      cacheTime.set(timeKey, curTime)
      time = curTime
    }
  }

  const key = timeKey + '#' + time
  // 远程获取
  let item = cache.get(key)
  if (item == null || item.error === true) {
    // 还没加载过
    if (item == null) {
      item = { loading: true, callbacks: [] }
      cache.set(key, item)
    }

    item.loading = true
    item.error = false

    // 远程加载
    return remoteFunc().then((data) => {
      item.data = data
      for (const callback of item.callbacks) {
        callback(item.data)
      }
      item.loading = false
      item.callbacks = []
      return data
    }).catch(() => {
      //catch中未处理callback ,存在callback永不执行的可能
      item.loading = false
      item.error = true
    })

  } else if (item.loading === true) {

    return new Promise((resolve) => {
      const callback = (data) => {
        resolve(data)
      }
      item.callbacks.push(callback)
    })
  } else {
    //  从缓存拿
    return new Promise((resolve) => {
      resolve(item.data)
    })
  }
}

const cacheTrans = new Map()

const cacheTransTime = new Map()
export function remoteTransRequest(prefix, id, timeout) {
  const curTime = new Date().getTime()
  if (!timeout) {
    timeout = REMOTE_TRANS_REQUEST_TIMEOUT
  }

  let time = cacheTransTime.get(prefix)
  if (time == null) {
    cacheTransTime.set(prefix, curTime)
    time = curTime
  }
  if (curTime - timeout >= time) { // 2秒内的请求都重新请求
    // 清除缓存换个请求
    cacheTransTime.clear()
    cacheTransTime.set(prefix, curTime)
    time = curTime
  }

  const key = prefix + '#' + time
  // 汇总接口
  let item = cacheTrans.get(key)

  let idVal = id
  if (Object.prototype.toString.call(id) === '[object Object]') {
    idVal = JSON.stringify(id)
  }

  if (item == null || item.error === true) {
    // 还没加载过
    if (item == null) {
      item = { loading: true, ids: new Set(), callbacks: [] }
      cacheTrans.set(key, item)
    }
    item.loading = true
    item.ids = item.ids.add(idVal)

    const remoteFunc = (ids) => {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve(ids)
        }, 100)
      })
    }

    return remoteFunc(item.ids).then((ids) => {
      item.ids = ids
      // 之前注册过的callback全部触发
      for (const callback of item.callbacks) {
        callback(ids)
      }
      item.loading = false
      item.callbacks = []

      return ids
    })
  } else if (item.loading === true) {

    return new Promise((resolve) => {
      const callback = (ids) => {
        item.ids = ids.add(idVal)
        resolve(item.ids)
      }
      item.callbacks.push(callback)
    })
  } else {
    //  从缓存拿
    return new Promise((resolve) => {
      resolve(item.ids)
    })
  }
}

解决:

//回调存在永不执行问题
return remoteFunc().then().catch((er)=>{
 for (const callback of item.callbacks) {
   callback(undefined, error); // Pass error to callback
 }
   item.callbacks = [];
})
//map对象无清理机制
export function des() {
  // 停止定时器
  stopCacheCleaner();
  
  // 清除所有缓存
  cache.clear();
  cache.clear();
  cacheTrans.clear();
  cacheTrans.clear();
}

概念

按需加载和懒加载和动态导入

都一样

按需加载(On-Demand Loading)

  • 按需加载通常指的是根据应用程序的需求和逻辑来加载资源。
  • 它可以是用户驱动的,例如,当用户点击一个按钮时加载一个模块,或者是系统驱动的,例如,当应用程序达到某个状态时自动加载某些数据。
  • 按需加载是一个广泛的概念,可以应用于各种资源,包括代码模块、数据、媒体文件等。

懒加载( Loading)

  • 懒加载是按需加载的一种形式,它特别强调延迟加载资源直到这些资源真正需要显示或使用时。
  • 懒加载经常用于图像、视频、脚本和其他页面元素,以减少初始页面加载时间和带宽使用。
  • 在Web开发中,懒加载通常与视图相关,例如,只有当用户滚动到页面的某个部分时,才加载那部分的图像或内容。

动态导入

  • js语法
  • import()

区别

  • 懒加载可以被视为按需加载的一个特定场景,它更多地关注于视图层面的资源和组件的加载。
  • 按需加载是一个更通用的术语,它可以涵盖从代码模块到数据的各种资源的加载策略。
  • 在实际应用中,懒加载通常是通过监听用户的滚动事件、视图的变化或其他交互行为来实现的,而按需加载可能是基于更复杂的逻辑或应用状态。
  • Dynamic import 是实现按需加载的技术手段

mmm9foJb
1 声望3 粉丝