微前端架构的几种技术选型

news2025/1/1 9:16:03

微前端架构的几种技术选型


随着SPA大规模的应用,紧接着就带来一个新问题:一个规模化应用需要拆分。

一方面功能快速增加导致打包时间成比例上升,而紧急发布时要求是越短越好,这是矛盾的。另一方面当一个代码库集成了所有功能时,日常协作绝对是非常困难的。而且最近十多年,前端技术的发展是非常快的,每隔两年就是一个时代,导致同志们必须升级项目甚至于换一个框架。但如果大家想在一个规模化应用中一个版本做好这件事,基本上是不可能的。

最早的解决方案是采用iframe的方法,根据功能主要模块拆分规模化应用,子应用之间使用跳转。但这个方案最大问题是导致页面重新加载和白屏。

那有什么好的解决方案呢?微前端这样具有跨应用的解决方案在此背景下应运而生了!

微前端的概念

微前端是什么:微前端是一种类似于微服务的架构,是一种由独立交付的多个前端应用组成整体的架构风格,将前端应用分解成一些更小、更简单的能够独立开发、测试、部署的应用,而在用户看来仍然是内聚的单个产品。有一个基座应用(主应用),来管理各个子应用的加载和卸载。

f135ab0912746bd6.png

所以微前端不是指具体的库,不是指具体的框架,不是指具体的工具,而是一种理想与架构模式。

微前端的核心三大原则就是:独立运行、独立部署、独立开发

微前端的优势

采用微前端架构的好处就是,将这些小型应用融合为一个完整的应用,或者将原本运行已久、没有关联的几个应用融合为一个应用可以将多个项目融合为一,又可以减少项目之间的耦合,提升项目扩展性。

实现微前端的几种方式

  • single-spaqiankun
  • 基于WebComponent的micro-app
  • webpack5实现的Module Federation

微前端框架的分类

Single-spa

single-spa是一个很好的微前端基础框架,而qiankun框架就是基于single-spa来实现的,在single-spa的基础上做了一层封装,也解决了single-spa的一些缺陷。

首先我们先来了解该如何使用single-spa来完成微前端的搭建。

single-spa.jpg

Single-spa实现原理

首先在基座应用中注册所有App的路由,single-spa保存各子应用的路由映射关系,充当微前端控制器Controler,。URL响应时,匹配子应用路由并加载渲染子应用。上图便是对single-spa完整的描述。

有了理论基础,接下来,我们来看看代码层面时如何使用的。

以下以Vue工程为例基座构建single-spa,在Vue工程入口文件main.js完成基座的配置。

基座配置

//main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import { registerApplication, start } from 'single-spa'

Vue.config.productionTip = false

const mountApp = (url) => {
  return new Promise((resolve, reject) => {
    const script = document.createElement('script')
    script.src = url

    script.onload = resolve
    script.onerror = reject

    // 通过插入script标签的方式挂载子应用
    const firstScript = document.getElementsByTagName('script')[0]
    // 挂载子应用
    firstScript.parentNode.insertBefore(script, firstScript)
  })
}

const loadApp = (appRouter, appName) => {

  // 远程加载子应用
  return async () => {
    //手动挂载子应用
    await mountApp(appRouter + '/js/chunk-vendors.js')
    await mountApp(appRouter + '/js/app.js')
    // 获取子应用生命周期函数
    return window[appName]
  }
}

// 子应用列表
const appList = [
  {
    // 子应用名称
    name: 'app1',
    // 挂载子应用
    app: loadApp('http://localhost:8083''app1'),
    // 匹配该子路由的条件
    activeWhen: location => location.pathname.startsWith('/app1'),
    // 传递给子应用的对象
    customProps: {}
  },
  {
    name: 'app2',
    app: loadApp('http://localhost:8082''app2'),
    activeWhen: location => location.pathname.startsWith('/app2'),
    customProps: {}
  }
]

// 注册子应用
appList.map(item => {
  registerApplication(item)
})
 
// 注册路由并启动基座
new Vue({
  router,
  mounted() {
    start()
  },
  render: h => h(App)
}).$mount('#app')


复制代码

构建基座的核心是:配置子应用信息,通过registerApplication注册子应用,在基座工程挂载阶段start启动基座。

子应用配置

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import singleSpaVue from 'single-spa-vue'

Vue.config.productionTip = false

const appOptions = {
  el: '#microApp',
  router,
  render: h => h(App)
}

// 支持应用独立运行、部署,不依赖于基座应用
// 如果不是微应用环境,即启动自身挂载的方式
if (!process.env.isMicro) {
  delete appOptions.el
  new Vue(appOptions).$mount('#app')
}
// 基于基座应用,导出生命周期函数
const appLifecycle = singleSpaVue({
  Vue,
  appOptions
})

// 抛出子应用生命周期
// 启动生命周期函数
export const bootstrap = (props)  => {
  console.log('app2 bootstrap')
  return appLifecycle.bootstrap(() => { })
}
// 挂载生命周期函数
export const mount = (props) => {
  console.log('app2 mount')
  return appLifecycle.mount(() => { })
}
// 卸载生命周期函数
export const unmount = (props) => {
  console.log('app2 unmount')
  return appLifecycle.unmount(() => { })
}

复制代码

配置子应用为umd打包方式

//vue.config.js
const package = require('./package.json')
module.exports = {
  // 告诉子应用在这个地址加载静态资源,否则会去基座应用的域名下加载
  publicPath: '//localhost:8082',
  // 开发服务器
  devServer: {
    port: 8082
  },
  configureWebpack: {
    // 导出umd格式的包,在全局对象上挂载属性package.name,基座应用需要通过这个
    // 全局对象获取一些信息,比如子应用导出的生命周期函数
    output: {
      // library的值在所有子应用中需要唯一
      library: package.name,
      libraryTarget: 'umd'
    }
  }

复制代码

配置子应用环境变量

// .env.micro 
NODE_ENV=development
VUE_APP_BASE_URL=/app2
isMicro=true
复制代码

子应用配置的核心是用singleSpaVue生成子路由配置后,必须要抛出其生命周期函数

用以上方式便可轻松实现一个简单的微前端应用了。

那么我们有single-spa这种微前端解决方案,为什么还需要qiankun呢?

相比于single-spaqiankun他解决了JS沙盒环境,不需要我们自己去进行处理。在single-spa的开发过程中,我们需要自己手动的去写调用子应用JS的方法(如上面的 createScript方法),而qiankun不需要,乾坤只需要你传入响应的apps的配置即可,会帮助我们去加载。

Qiankun

Qiankun的优势

基于 single-spa[1] 封装,提供了更加开箱即用的 API。

技术栈无关,任意技术栈的应用均可 使用/接入,不论是 React/Vue/Angular/JQuery 还是其他等框架。

HTML Entry 接入方式,让你接入微应用像使用 iframe 一样简单。

样式隔离,确保微应用之间样式互相不干扰。

JS 沙箱,确保微应用之间 全局变量/事件 不冲突。

资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。

基座配置

import { registerMicroApps, start } from 'qiankun';


registerMicroApps([
  {
    name: 'reactApp',
    entry: '//localhost:3000',
    container: '#container',
    activeRule: '/app-react',
  },
  {
    name: 'vueApp',
    entry: '//localhost:8080',
    container: '#container',
    activeRule: '/app-vue',
  },
  {
    name: 'angularApp',
    entry: '//localhost:4200',
    container: '#container',
    activeRule: '/app-angular',
  },
]);
// 启动 qiankun
start();
复制代码

子应用配置

以 create react app 生成的 react 16 项目为例,搭配 react-router-dom 5.x。

1.在 src 目录新增 public-path.js,解决子应用挂载时,访问静态资源冲突

  if (window.__POWERED_BY_QIANKUN__) {
    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
  }
复制代码

2.设置 history 模式路由的 base

  <BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/app-react' : '/'}>
复制代码

3.入口文件 index.js 修改,为了避免根 id #root 与其他的 DOM 冲突,需要限制查找范围。

  import './public-path';
  import React from 'react';
  import ReactDOM from 'react-dom';
  import App from './App';


  function render(props) {
    const { container } = props;
    ReactDOM.render(<App />, container ? container.querySelector('#root') : 
    document.querySelector('#root'));
  }


  if (!window.__POWERED_BY_QIANKUN__) {
    render({});
  }


  export async function bootstrap() {
    console.log('[react16] react app bootstraped');
  }


  export async function mount(props) {
    console.log('[react16] props from main framework', props);
    render(props);
  }


  export async function unmount(props) {
    const { container } = props;
    ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') :  
    document.querySelector('#root'));
  }
复制代码

4.修改 webpack 配置

安装插件 @rescripts/cli,当然也可以选择其他的插件,例如 react-app-rewired

npm i -D @rescripts/cli
复制代码

根目录新增 .rescriptsrc.js

const { name } = require('./package');


module.exports = {
  webpack: (config) => {
    config.output.library = `${name}-[name]`;
    config.output.libraryTarget = 'umd';
    config.output.jsonpFunction = `webpackJsonp_${name}`;
    config.output.globalObject = 'window';


    return config;
  },


  devServer: (_) => {
    const config = _;


    config.headers = {
      'Access-Control-Allow-Origin''*',
    };
    config.historyApiFallback = true;
    config.hot = false;
    config.watchContentBase = false;
    config.liveReload = false;


    return config;
  },
};
复制代码

以上对Qiankun的使用可以看出,与single-spa使用过程很相似。不同的是,Qiankun的使用过程更简便了。一些内置的操作交由给Qiankun内部实现。这是一种IOC思想的实现,我们只管面向容器化开发,其他操作交给Qiankun框架管理。

Micro-app

micro-app并没有沿袭single-spa的思路,而是借鉴了WebComponent的思想,通过CustomElement结合自定义的ShadowDom,将微前端封装成一个类WebComponent组件,从而实现微前端的组件化渲染。并且由于自定义ShadowDom的隔离特性,micro-app不需要像single-spaqiankun一样要求子应用修改渲染逻辑并暴露出方法,也不需要修改webpack配置,是目前市面上接入微前端成本最低的方案。

WebComponent的概念

**WebComponent**[2]是HTML5提供的一套自定义元素的接口,**WebComponent**[3]是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的 web 应用中使用它们。以上是MDN社区对WebComponent的解释。

  • Custom elements(自定义元素): 一组 JavaScript API,允许您定义 custom elements 及其行为,然后可以在您的用户界面中按照需要使用它们。
  • Shadow DOM(影子 DOM) :一组 JavaScript API,用于将封装的“影子”DOM 树附加到元素(与主文档 DOM 分开呈现)并控制其关联的功能。通过这种方式,您可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。
  • HTML templates(HTML 模板):  <template> 和 <slot> 元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。

接下来用一个小例子更快来理解WebComponent的概念。

一个存在组件内交互的WebComponent

// 基于HTMLElement自定义组件元素
class CounterElement extends HTMLElement {

  // 在构造器中生成shadow节点
  constructor() {
    super();

    this.counter = 0;

    // 打开影子节点
    // 影子节点是为了隔离外部元素的影响
    const shadowRoot = this.attachShadow({ mode: 'open' });

    // 定义组件内嵌样式
    const styles = `
          #counter-increment {
              width: 60px;
              height: 30px;
              margin: 20px;
              background: none;
              border: 1px solid black;
          }
      `;

    // 定义组件HTMl结构
    shadowRoot.innerHTML = `
          <style>${styles}</style>
          <h3>Counter</h3>
          <slot name='counter-content'>Button</slot>
          <span id='counter-value'>; 0 </span>;
          <button id='counter-increment'> + </button>
      `;

    // 获取+号按钮及数值内容
    this.incrementButton = this.shadowRoot.querySelector('#counter-increment');
    this.counterValue = this.shadowRoot.querySelector('#counter-value');

    // 实现点击组件内事件驱动
    this.incrementButton.addEventListener("click"this.decrement.bind(this));

  }

  increment() {
    this.counter++
    this.updateValue();
  }

  // 替换counter节点内容,达到更新数值的效果
  updateValue() {
    this.counterValue.innerHTML = this.counter;
  }
}

// 在真实dom上,生成自定义组件元素
customElements.define('counter-element', CounterElement);

复制代码

有了对WebComponent的理解,接下来,我们更明白了Micro-app的优势。

micro-app的优势

d879637b4bb34253.png

使用简单

我们将所有功能都封装到一个类WebComponent组件中,从而实现在基座应用中嵌入一行代码即可渲染一个微前端应用。

同时micro-app还提供了js沙箱样式隔离元素隔离预加载数据通信静态资源补全等一系列完善的功能。

零依赖

micro-app没有任何依赖,这赋予它小巧的体积和更高的扩展性。

兼容所有框架

为了保证各个业务之间独立开发、独立部署的能力,micro-app做了诸多兼容,在任何技术框架中都可以正常运行。

基座的简易配置

基座存在预加载子应用、父子应用通信、公共文件共享等等


// index.js
import React from "react"
import ReactDOM from "react-dom"
import App from './App'
import microApp from '@micro-zoe/micro-app'

const appName = 'my-app'

// 预加载
microApp.preFetch([
  { name: appName, url: 'xxx' }
])

// 基座向子应用数据通信
microApp.setData(appName, { type: '新的数据' })
// 获取指定子应用数据
const childData = microApp.getData(appName)

microApp.start({
  // 公共文件共享
  globalAssets: {
    js: ['js地址1''js地址2', ...], // js地址
    css: ['css地址1''css地址2', ...], // css地址
  }
})
复制代码

分配一个路由给子应用

// router.js
import { BrowserRouter, Switch, Route } from 'react-router-dom'

export default function AppRoute () {
  return (
    <BrowserRouter>
      <Switch>
        <Route path='/'>
          <micro-app name='app1' url='http://localhost:3000/' baseroute='/'></micro-app>
        </Route>
      </Switch>
    </BrowserRouter>
  )
}

复制代码

子应用的简易配置

// index.js
import React from "react"
import ReactDOM from "react-dom"
import App from './App'
import microApp from '@micro-zoe/micro-app'

const appName = 'my-app'

// 子应用运行时,切换静态资源访问路径
if (window.__MICRO_APP_ENVIRONMENT__) {
  __webpack_public_path__ = window.__MICRO_APP_PUBLIC_PATH__
}

// 基子应用向基座发送数据
// dispatch只接受对象作为参数
window.microApp.dispatch({ type: '子应用发送的数据' })
// 获取基座数据
const data = window.microApp.getData() // 返回基座下发的data数据

//性能优化,umd模式
// 如果子应用渲染和卸载不频繁,那么使用默认模式即可,如果子应用渲染和卸载非常频繁建议使用umd模式
// 将渲染操作放入 mount 函数 -- 必填
export function mount() {
  ReactDOM.render(<App />document.getElementById("root"))
}

// 将卸载操作放入 unmount 函数 -- 必填
export function unmount() {
  ReactDOM.unmountComponentAtNode(document.getElementById("root"))
}

// 微前端环境下,注册mount和unmount方法
if (window.__MICRO_APP_ENVIRONMENT__) {
  window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
else {
  // 非微前端环境直接渲染
  mount()
}

复制代码

设置子应用路由

import { BrowserRouter, Switch, Route } from 'react-router-dom'

export default function AppRoute () {
  return (
    // 设置基础路由,子应用可以通过window.__MICRO_APP_BASE_ROUTE__获取基座下发的baseroute,
    // 如果没有设置baseroute属性,则此值默认为空字符串
    <BrowserRouter basename={window.__MICRO_APP_BASE_ROUTE__ || '/'}>
      ...
    </BrowserRouter>
  )
}

复制代码

以上便是Micro-app的用法

Module Federation

Module Federation是Webpack5提出的概念,module federation用来解决多个应用之间代码共享的问题,让我们更加优雅的实现跨应用的代码共享。

MF想做的事和微前端想解决的问题是类似的,把一个应用进行拆分成多个应用,每个应用可独立开发,独立部署,一个应用可以动态加载并运行另一个应用的代码,并实现应用之间的依赖共享。

为了实现这样的功能, MF在设计上提出了这几个核心概念。

Container

一个被 ModuleFederationPlugin 打包出来的模块被称为 Container。通俗点讲就是,如果我们的一个应用使用了 ModuleFederationPlugin 构建,那么它就成为一个 Container,它可以加载其他的 Container,可以被其他的 Container 所加载。

Host&Remote

从消费者和生产者的角度看 ContainerContainer 又可被称作 Host 或 Remote

Host:消费方,它动态加载并运行其他 Container 的代码。

Remote:提供方,它暴露属性(如组件、方法等)供 Host 使用

可以知道,这里的 Host 和 Remote 是相对的,因为 一个 Container 既可以作为 Host,也可以作为 Remote

Shared

一个 Container 可以 Shared 它的依赖(如 react、react-dom)给其他 Container 使用,也就是共享依赖。

微信图片_20220626184254.png

微信图片_20220626184305.png

以上是webpack5与之前版本的模块管理对比图

微应用配置

通过webpack5的配置达成微应用的效果

// 配置webpack.config.js
const { ModuleFederationPlugin } = require("webpack").container;
new ModuleFederationPlugin({
  name: "appA",
 //出口文件
  filename: "remoteEntry.js",
 //暴露可访问的组件
  exposes: {
    "./input""./src/input",
  },
 //或者其他模块的组件
 //如果把这一模块当作基座模块的话,
 //这里应该配置其他子应用模块的入口文件
  remotes: {
    appB: "appB@http://localhost:3002/remoteEntry.js",
  },
 //共享依赖,其他模块不需要再次下载,便可使用
  shared: ['react''react-dom'],
})

复制代码

以上便是我对微应用架构的理解,以及微应用架构技术的演变过程。不难看出,这些技术的演变都朝着易用性和可拓展性的方向演进。其中技术也有其时代的局限性,不过思想和技术总是在不断进步的。这几类技术选型都有其优缺点,各有千秋,我们可以根据不同的需要选择不同的技术来构建应用。

下列是本文写作时的参考资料:

single-spa: zh-hans.single-spa.js.org/docs/gettin…[4]

qiankun: qiankun.umijs.org/zh/guide[5]

WebComponent: developer.mozilla.org/zh-CN/docs/…[6]

micro-app: cangdu.org/micro-app/d…[7]

参考资料

[1]

https://github.com/CanopyTax/single-spa

[2]

https://developer.mozilla.org/zh-CN/docs/Web/Web_Components#%E4%BE%8B%E5%AD%90

[3]

https://developer.mozilla.org/zh-CN/docs/Web/Web_Components#%E4%BE%8B%E5%AD%90

[4]

https://zh-hans.single-spa.js.org/docs/getting-started-overview

[5]

https://qiankun.umijs.org/zh/guide

[6]

https://developer.mozilla.org/zh-CN/docs/Web/Web_Components

[7]

http://cangdu.org/micro-app/docs.html#/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1030984.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【校招VIP】java语言考点之反射

考点介绍&#xff1a; java的反射(reflection)机制是指在程序的运行状态中&#xff0c;可以构造任意一个类的对象&#xff0c;可以了解任意一个对象所属的类&#xff0c;可以了解任意一个类的成员变量和方法&#xff0c;可以调用任意一个对象的属性和方法。这种动态获取程序信息…

探索工业路由器如何助力无人驾驶方案的突破性解析

随着无人驾驶技术的发展&#xff0c;越来越多的企业和组织开始部署无人驾驶车辆来提高运输效率和安全性。在这些方案中&#xff0c;工业路由器被广泛应用于建立稳定、安全和高效的通信网络。在本篇文章中&#xff0c;我们将分享一个真实的无人驾驶方案部署案例&#xff0c;其中…

laravel设置与获取header请求头

laravel设置与获取header请求头 设置 <?phpnamespace App\Http\Controllers\Text;use Illuminate\Http\Request; use App\Http\Controllers\Controller;class TextController extends Controller {public function TextCC(Request $request){$token $request->header(j…

【函数进阶】

函数进阶 1 本节目标2 函数的定义和调用2.1 函数的定义方式2.2 函数的调用方式 3 this3.1 函数内 this 的指向3.2 改变函数内部 this 的指向3.2.1 call方法3.2.2 apply方法3.2.3 bind方法3.2.4 call apply bind 总结 4 严格模式4.1 什么是严格模式4.2 开启严格模式4.2.1 为脚本…

【校招VIP】交流技巧之面试时合理表达观点

考点介绍&#xff1a; 交流和表达是产品的面试最重要的考查点之一&#xff0c;也是产品必备工作技能。如果在面试中不能合理的与面试官沟通&#xff0c;或者不能把自己的思路和分析有逻辑的表达出来&#xff0c;都会对面试结果产生不好的影响。 交流技巧之面试时合理表达观点-…

【网络协议】Http-下

因为Http是无状态的&#xff0c;所以为了协助 Web 保持状态&#xff0c;Cookie 诞生了。 下面中是百度百科关于Cookie和Session的解释&#xff1a; Cookie&#xff1a;举例来说, 一个 Web 站点可能会为每一个访问者产生一个唯一的ID, 然后以 Cookie 文件的形式保存在每个用户的…

序列化 qmap

自定义的map不得行 但是qmap可以自己分配具体内存 这里没照完 待会照

通讯网关软件009——利用CommGate X2MQTT实现MQTT访问ODBC数据源

本文介绍利用CommGate X2MQTT实现MQTT访问ODBC数据源。CommGate X2MQTT是宁波科安网信开发的网关软件&#xff0c;软件可以登录到网信智汇(http://wangxinzhihui.com)下载。 【案例】如下图所示&#xff0c;实现上位机通过MQTT来获取ODBC数据源的数据。 【解决方案】设置网关机…

「Qt中文教程指南」如何创建基于Qt Widget的应用程序(一)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 本文描述了如何使用…

【Java 并发编程】CopyOnWriterArrayList 详解

CopyOnWriterArrayList 详解 1. ArrayList1.1 ArrayList 和 LinkedList 的区别1.2 ArrayList 如何保证线程安全 2. CopyOnWriteArrayList 原理3. CopyOnWriteArrayList 的优缺点3.1 优点3.2 缺点 4. 源码分析4.1 两个成员变量4.2 构造函数4.3 add(E e)4.4 add(int index, E ele…

(总目录)springboot - 实现zip文件上传并对zip文件解压, 包含上传oss

全文目录,一步到位 1.本文概述1.1 本文简介 2. 功能实现2.1 统一文件校验2.2 普通(多)文件上传[服务器]2.2.1 controller层2.2.2 service层2.2.3 业务impl实现类2.2.4 FileIOUtils工具包代码 2.3 zip文件的解压2.4 图片文件的压缩2.5 oss文件后端上传2.6 oss文件前端上传2.7 后…

传统企业如何实现数字化转型?

传统企业实现数字化转型是一个复杂且多方面的过程&#xff0c;涉及将数字技术和战略融入业务的各个方面&#xff0c;以推动创新、效率和竞争力。以下是传统企业实现数字化转型可以遵循的步骤和策略&#xff1a; 1.领导层的认可和愿景&#xff1a; 首先要确保最高领导层&#x…

SpringCloud Gateway搭建Gateway 微服务应用实例

&#x1f600;前言 本篇博文是关于SpringCloud Gateway搭建Gateway 微服务应用实例&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您…

前端JavaScript入门到精通,javascript核心进阶ES6语法、API、js高级等基础知识和实战 —— JS基础(三)

思维导图 一、循环-for 1.1 for 循环-基本使用 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport"…

scryptTS文档搜索功能上线!

在 scryptTS 文档中搜索 随着 scryptTS 文档的内容越来越丰富&#xff0c;从大量资料中快速定位感兴趣的部分变得越来越困难。 现在&#xff0c;你可以使用搜索功能&#xff0c;快速查找想了解的内容。

金蝶云星空与聚水潭对接集成物料查询连通商品上传(新)(物料主数据同步策略)

金蝶云星空与聚水潭对接集成物料查询连通商品上传&#xff08;新&#xff09;(物料主数据同步策略) 数据源系统:金蝶云星空 金蝶K/3Cloud结合当今先进管理理论和数十万家国内客户最佳应用实践&#xff0c;面向事业部制、多地点、多工厂等运营协同与管控型企业及集团公司&#x…

肖sir__项目环境之全流程__005

一、测试流程&#xff08;h模型&#xff09; 1、需求文档&#xff08;产品&#xff09; 需求文档&#xff08;软件需求规格说明书srs&#xff09; &#xff08;1&#xff09;如何分析需求 a、显示需求&#xff08;主流程、功能&#xff0c;业务&#xff09; b、隐性需求&#x…

java Spring Boot2.7实现一个简单的爬虫功能

首先 我们要在 pom.xml 中注入Jsoup 这是一个简单的java爬虫框架 <dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.14.1</version> </dependency>然后这里我们直接用main吧 做简单一点 我…

NSDT孪生场景编辑器系统介绍

一、产品背景 数字孪生的建设流程涉及建模、美术、程序、仿真等多种人才的协同作业&#xff0c;人力要求高&#xff0c;实施成本高&#xff0c;建设周期长。如何让小型团队甚至一个人就可以完成数字孪生的开发&#xff0c;是数字孪生工具链要解决的重要问题。考虑到数字孪生复杂…

Flask框架-2-[单聊]: flask-socketio实现websocket的功能,实现单对单聊天,flask实现单聊功能

一、概述和项目结构 在使用flask-socketio实现单聊时&#xff0c;需要将会话id(sid) 与用户进行绑定&#xff0c;通过emit(事件,消息,tosid) ,就可以把消息单独发送给某个用户了。 flask_websocket |--static |--js |--jquery-3.7.0.min.js |--socket.io_4.3.1.js |--template…