当前位置: 首页 > news >正文

vue脚手架多页自动化生成实践

在这里插入图片描述

前言

在前端开发过程中,常常面对多种业务场景。到目前为止,前端对于不同场景的处理通常会采用不同的渲染方案来组合处理,常见的渲染方案包括:CSR(Client Side Rendering)、SSR(Server Side Rendering)、SSG(Static Site Generation)、ISR(Incremental Site Rendering)、DPR(Distributed Persistent Rendering)、NSR(Native Side Rendering)以及ESR(Edge Side Rendering)等。在目前项目开发过程中,遇到了需要构建门户类应用的需求,而团队主要技术栈以Vue为主,整个技术方案以Vue全家桶进行构建。因此,本文旨在针对门户类应用的场景下的Vue脚手架构建方案的一些总结和分析,通过自动化的配置脚本来生成模板化的多页应用实践,以期能够给读者提供一个基于Vue全家桶的门户类工程构建方案。

架构

图片

对于门户类型的应用,由于其大部分内容变动内容较少,而对于部分关键页面却会有动态更新的要求,因而在通常会采用多页形式的处理配合部分单页应用中的优势进行处理。因而,在技术选型方面,团队采用了预渲染配合多页的方式实现门户类SEO及首屏加载快的需求。同时,结合单页应用的优势,在多页中的部分关键页面中采用单页中的优点,如:路由切换快、用户体验好等。综上,架构风格采用ISR的增量渲染方案,由于项目背景的特殊性,无法配合常规CDN等部署方案特点,但可以使用云原生相关的中间件实现类似效果,整体部署仍以“云+端”的形式为主。

目录

selfService├─portal
├─ build                                // vue cli打包所需的options中内容一些抽离,对其中做了环境区分
|   ├─ demo
|   |    ├─config.json
|   |    ├─configureWebpack.js
|   ├─ dev
|   |    ├─ config.json
|   |    ├─ configureWebpack.js
|   ├─ production
|   |    ├─ config.json
|   |    ├─ configureWebpack.js
|   ├─ chainWebpack.js
|   ├─ configureWebpack.js
|   ├─ devServer.js
|   ├─ pages.js
|   ├─ routes.js
|   ├─ index.js
|   ├─ utils.js
├─ deploy                                 // 不同环境的部署
|   ├─ demo
|   |    ├─ default.conf
|   |    ├─ Dockerfile
|   |    ├─ env.sh
|   ├─ dev
|   |    ├─ default.conf
|   |    ├─ Dockerfile
|   |    ├─ env.sh
|   ├─ production
|   |    ├─ default.conf
|   |    ├─ Dockerfile
|   |    ├─ env.sh
|   ├─ build.sh
├─ public
|   ├─ pageA                              // pageA的html,这里可以存放一些静态资源,非构建状态下的js、css等
|   |    ├─ index.html
|   ├─ pageB                              // pageB的html,这里可以存放一些静态资源,非构建状态下的js、css等
|   |    ├─ index.html
|   ├─ favicon.ico
├─ src
|   ├─ assets                             // 存放小资源,通常为必须,如:logo等,其他静态资源请放入cdn或者public下
|   |    ├─ logo.png
|   ├─ components                         // 公共组件,可抽离多个静态页面的公共组件
|   |    ├─ Header.vue
|   ├─ router
|   |    ├─ pageA                         // pageA的router,使用了history模式
|   |        ├─ index.js
|   |    ├─ pageB                         // pageB的router,使用了history模式
|   |        ├─ index.js
|   ├─ store
|   |    ├─ pageA                         // pageA的Vuex
|   |        ├─ index.js
|   |    ├─ pageB                         // pageB的Vuex
|   |        ├─ index.js
|   ├─ views
|   |    ├─ pageA                         // pageA的页面,写法和之前一个的单页应用一致
|   |        ├─ main.js                   // 注入了mode,挂载到了vue的原型上,使用this可以获取环境变量
|   |        ├─ pageA.vue
|   |    ├─ pageB                         // pageB的页面,写法和之前一个的单页应用一致
|   |        ├─ main.js                   // 注入了mode,挂载到了vue的原型上,使用this可以获取环境变量
|   |        ├─ pageB.vue
├─ scripts                 
├─ babel.config.js                        // 配置es转化语法
├─ vue.config.js                          // vue cli打包相关配置
├─ app.json                               // 存放各个多页应用的public、router、vuex、views入口地址

实践

配置

在这里插入图片描述

Vue脚手架中配置多页主要是使用Webpack中的pages入口配置,这里主要是修改vue.config.js中的pages的设置,代码如下:

const PrerenderSPAPlugin = require('prerender-spa-plugin');
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer;
module.exports = {// ...pages: {page3:{entry: "src/views/page3/main.js",template: "public/page3/index.html",filename: "page3.html",title: "page3"}},configureWebpack: config => {config.plugins.push(new PrerenderSPAPlugin({staticDir: path.resolve(__dirname,'../../dist'),routes: ['/page3'],renderer: new Renderer({less: false,//renderAfterDocumentEvent: 'render-event',//renderAfterTime: 5000,//renderAfterElementExists: 'my-app-element'}),}))} 
}

其中,如果配置了pages,@vue/cli-service会先清除原有的entry,如果没有index,则devServer默认入口的根路径’/‘仍为index.html;如果有index的key值,则会进行相应的覆盖。在这里,对pages下的key值为对应多页的路径,如:上述代码下的page3,则对应的路径为’/page3.html’;pages下的value可以为字符串,也可以为对象,其中:entry为多页的入口(必选项)、template为模板来源、filename为打包后的输出名称以及title会通过html-webpack-plugin的插件对template中的<title><%= htmlWebpackPlugin.options.title %></title>进行替换。

而对于预渲染的应用,这里使用了prerender-spa-plugin和vue-meta-info来进行SEO及首屏加载优化,代码如下:

// ...
import MetaInfo from 'vue-meta-info'Vue.use(MetaInfo)new Vue({router,store,render: h => h(index),mounted () {document.dispatchEvent(new Event('custom-render-trigger'))}
}).$mount('#page3')

脚本

在这里插入图片描述

通过上述的配置,基本就可以实现一个 预渲染+多页 的vue脚手架搭建。但是,除了开发环境的配置,对于生产环境、部署等也需要进行一定的设置,这样频繁的操作就会带来一定的功效降低。因而,在前端工程化领域中,通常会进行一定的脚本化或者说脚手架方案的构建。这里,在目前项目中,团队对多页应用的配置进行了自动化的脚本实现。

生成多页的脚本主要通过page.js进行实现,代码如下:

const inquirer  = require('inquirer');
const chalk = require('chalk');
const fs = require('fs');
const path = require('path');
const ora = require('ora');
const { transform, compose } = require('./utils');const spinner = ora();const PAGE_REG = /[a-zA-Z_0-9]/ig;
const rootDir = path.resolve(process.cwd(), '.');// 判断dir目录下是否存在name的文件夹
const isExt = (dir, name)  => fs.existsSync(path.join(dir, name));const APP_JSON_EJS = `{"pages": <%= page_name %> 
}`;const INDEX_HTML_EJS = `<!DOCTYPE html>
<html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1.0"><link rel="icon" href="../favicon.ico"><title><%= htmlWebpackPlugin.options.title %></title></head><body><noscript><strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><%= page_name %></body>
</html>
`const INDEX_VUE_EJS = `<%= page_name %><script>
export default {
components: {
},
data() {return {};
},
};
</script><style lang="less">
</style>`const MAIN_JS_EJS = `<%= page_name %>`const INDEX_ROUTER_EJS = `import Vue from 'vue'
import VueRouter from 'vue-router'Vue.use(VueRouter)const routes = [<%= page_name %>
]const router = new VueRouter({mode: 'history',routes
})export default router`const INDEX_STORE_EJS = `import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {},mutations: {},actions: {},modules: {}
})
`// inquirer list
const promptList = [{type: 'input',name: 'page_name',message: '请输入你想要创建的多页应用名称',filter: function (v) {return v.match(PAGE_REG).join('')}}
];// nginx的default.conf所需添加内容
const addDefaultConf = page_name => {return `location /${page_name} {root   /usr/share/nginx/html;index  ${page_name}.html;try_files $uri $uri/ /${page_name}.html;gzip_static on;
}`
};// page_name下的index.html
const addIndexHtml = page_name => {return `<div id="${page_name}" data-server-rendered="true"></div>`
};// page_name下的router
const addRouterIndex = page_name => {return `{path: '/',component: () => import('../../views/${page_name}/index.vue')
},`
};// page_name下的views index.vue
const addViewsIndex = page_name => {return `<template><div>${page_name}</div>
</template>`
};// page_name下的views main.js
const addViewsMain = page_name => {return `import Vue from 'vue'
import index from './index.vue'
import router from '../../router/${page_name}/index.js'
import store from '../../store/${page_name}/index.js'
import MetaInfo from 'vue-meta-info'Vue.use(MetaInfo)import axios from 'axios'Vue.prototype.$mode = process.env.VUE_APP_MODE;Vue.prototype.axios = axios;Vue.config.productionTip = falsenew Vue({router,store,render: h => h(index),mounted () {document.dispatchEvent(new Event('custom-render-trigger'))}
}).$mount('#${page_name}')`
};// page_name下的pages.js
const addPages = page_name => {return JSON.stringify({entry: `src/views/${page_name}/main.js`,template: `public/${page_name}/index.html`,filename: `${page_name}.html`,title: `${page_name}`,})
}const updateApp = page_name => {// 获取pages的数组const pages = require('../app.json')['pages'];if(pages.includes(page_name)) return true;pages.push(page_name);spinner.start()fs.writeFile(`${rootDir}/app.json`, transform(/<%= page_name %>/g, JSON.stringify(pages), APP_JSON_EJS), err => {spinner.color = 'red';spinner.text = 'Loading Update app.json'if(err) {spinner.fail(chalk.red(`更新app.json失败`))return false;} else {spinner.succeed(chalk.green(`更新app.json成功`))return true;}});
}// 处理 public 文件夹下的核心逻辑
const processPublic = args => {const { page_name } = args;if(isExt(`${rootDir}/public`, page_name)) {return args;} else {fs.mkdirSync(`${rootDir}/public/${page_name}`)}fs.writeFileSync(`${rootDir}/public/${page_name}/index.html`, transform(/<%= page_name %>/g, addIndexHtml(page_name), INDEX_HTML_EJS));// 处理默认页面的跳转const content = require('../app.json')['pages'].map(page => {return `<li><a href="/${page}.html">${page}</a>
</li>`}).join(`
`);const ejs_arr = fs.readFileSync(`${rootDir}/public/index.html`, 'utf-8').split(`<body>`);fs.writeFileSync(`${rootDir}/public/index.html`, ejs_arr[0] + `<body>
`+`<h1>自服务门户</h1>
<ul>${content}
</ul>` + `
</body>
</html>`);return args;
};// 处理 src/views 文件夹下的核心逻辑
const processViews = args => {const { page_name } = args;if(isExt(`${rootDir}/src/views`, page_name)) {return args;} else {fs.mkdirSync(`${rootDir}/src/views/${page_name}`)}fs.writeFileSync(`${rootDir}/src/views/${page_name}/index.vue`, transform(/<%= page_name %>/g, addViewsIndex(page_name), INDEX_VUE_EJS));fs.writeFileSync(`${rootDir}/src/views/${page_name}/main.js`, transform(/<%= page_name %>/g, addViewsMain(page_name), MAIN_JS_EJS));return args;
};// 处理 src/router 文件夹下的核心逻辑
const processRouter = args => {const { page_name } = args;if(isExt(`${rootDir}/src/router`, page_name)) {return args;} else {fs.mkdirSync(`${rootDir}/src/router/${page_name}`)}fs.writeFileSync(`${rootDir}/src/router/${page_name}/index.js`, transform(/<%= page_name %>/g, addRouterIndex(page_name), INDEX_ROUTER_EJS));return args;
};// 处理 src/store 文件夹下的核心逻辑
const processStore = args => {const { page_name } = args;if(isExt(`${rootDir}/src/store`, page_name)) {return args;} else {fs.mkdirSync(`${rootDir}/src/store/${page_name}`)}fs.writeFileSync(`${rootDir}/src/store/${page_name}/index.js`, INDEX_STORE_EJS);return args;
};// 处理 build 文件夹下的核心逻辑
const processBuild = args => {const { page_name } = args;// 处理 build/page.jsconst pages  = require('../build/pages.js');if(Object.keys(pages).includes(page_name)) return args;pages[`${page_name}`] = JSON.parse(addPages(page_name));const PAGES_JS_EJS =`const pages = ${JSON.stringify(pages)}
module.exports = pages;`;fs.writeFileSync(`${rootDir}/build/pages.js`, PAGES_JS_EJS);// 处理 build/routes.jsconst routes = require('../build/routes.js');if(routes.includes(`/${page_name}`)) return args;routes.push(`/${page_name}`);const ROUTES_JS_EJS =`const pages = ${JSON.stringify(routes)}
module.exports = pages;`;fs.writeFileSync(`${rootDir}/build/routes.js`, ROUTES_JS_EJS);return args;
}// 处理 deploy 文件夹下的核心逻辑
const processDeploy = args => {const { page_name } = args;const reg = new RegExp(`location /${page_name}`);['demo', 'dev', 'production'].forEach(item => {const content = fs.readFileSync(`${rootDir}/deploy/${item}/default.conf`, 'utf-8');if(reg.test(content)) return args;const ejs_arr = content.split(`location  /api/`)fs.writeFileSync(`${rootDir}/deploy/${item}/default.conf`, transform(/<%= page_name %>/g, addDefaultConf(page_name), ejs_arr[0] + `<%= page_name %>
location  /api/`+ ejs_arr[1]));});return args;
};inquirer.prompt(promptList).then(answers => {const page_name = answers.page_name;return updateApp(page_name)}).then(() => {const pages = require('../app.json')['pages'];pages.forEach(page => {console.log('page', page)compose(processDeploy,processBuild, processStore, processRouter, processViews, processPublic)({page_name: page});})}).catch(err => {if(err) {console.log(chalk.red(err))}})

为了更好的实现代码的优雅性,对代码工具进行了抽离,放入到utils.js中,代码如下:

// 将内容替换进ejs占位符
const transform = ($, content, ejs) => ejs.replace($,content);// 将流程串联
const compose = (...args) => args.reduce((prev,current) => (...values) => prev(current(...values)));module.exports = {transform,compose
}

总结

仅管到目前为止,单页应用仍是前端开发中的主流方案。但是,随着各大应用的复杂度提升,多种方案的建设也都有了来自业界不同的声音,诸如:多种渲染方案、Island架构等都是为了能更好的提升Web领域的体验与开发建设。技术方案的选择不只局限于生态的整合,更重要的是对合适场景的合理应用。

“形而上者谓之道,形而下者谓之器”,各位前端开发者不仅应该只着眼于眼前的业务实现,同时也需要展望未来,站在更高的视野上来俯视技术的走向与演进,共勉!!!

参考

  • vue-cli搭建自动化多页面项目(vue高阶)
  • Vue-cli配置多页面
  • vue预渲染之prerender-spa-plugin插件应用
  • 预渲染插件prerender-spa-plugin生成多页面
  • CSR、SSR、NSR、ESR傻傻分不清楚,一文帮你理清前端渲染方案!
  • vue项目改造SSR(服务端渲染)
  • 什么是SSR/SSG/ISR?如何在AWS上托管它们?
  • 卷起来,前端建站SSG,SSR,ISR,Hydration, Island…一网打尽
  • 你知道吗?SSR、SSG、ISR、DPR 有什么区别?
  • SSR、ISR、CSR、SSG有什么区别
  • 一文看懂Next.js渲染方法:CSR、SSR、SSG和ISR

相关文章:

vue脚手架多页自动化生成实践

前言 在前端开发过程中&#xff0c;常常面对多种业务场景。到目前为止&#xff0c;前端对于不同场景的处理通常会采用不同的渲染方案来组合处理&#xff0c;常见的渲染方案包括&#xff1a;CSR(Client Side Rendering)、SSR(Server Side Rendering)、SSG(Static Site Generati…...

【SQL语句优化】

SQL语句优化是提高数据库查询性能的重要手段之一&#xff0c;下面是几种常见的SQL语句优化方法和案例&#xff1a; 减少查询的数据量 减少查询的数据量&#xff1a;使用 WHERE 子句和索引来限制检索行数&#xff0c;只检索需要的行&#xff0c;避免检索全部行数据。 例子&am…...

阿里P8:做测试10年我的一些经验分享,希望你们少走弯路

我是在2015年毕业的&#xff0c;当时是读的普通本科&#xff0c;不上不下的专业水平&#xff0c;毕业的时候&#xff0c;恰好遇到了金融危机。校园招聘里阴差阳错的巧合&#xff0c;让我走上了软件测试工程师的道路。 入职第一天&#xff0c;来了个高大上的讲师&#xff0c;记…...

栈在括号匹配中的应用(栈/链栈 纯C实现)

目录 1 问题背景 2 具体思路 3 代码实现 3.1 顺序栈实现 3.2 链栈实现 1 问题背景 栈的括号匹配问题是指在给定一个字符串&#xff08;包含多种括号&#xff09;&#xff0c;判断其中的括号是否能够正确匹配&#xff0c;即每个左括号是否有一个对应的右括号与之匹配&#x…...

C语言Switch语句用法

C switch 语句 一个 switch 语句允许测试一个变量等于多个值时的情况。每个值称为一个 case&#xff0c;且被测试的变量会对每个 switch case 进行检查。 语法 C 语言中 switch 语句的语法&#xff1a; switch(expression){case constant-expression :statement(s);break;…...

Curl编码请求参数,API接口请求示例参数

请求参数请求参数&#xff1a;num_iid610947572360 参数说明&#xff1a;num_iid:1688商品ID sales_data:&sales_data1 获取近30天成交数据 agent:&agent1 获取1688分销代发价格数据请求示例 测试入口 Curl PHP PHPsdk JAVA C# Python-- 请求示例 url 默认请求参数已经…...

【C/C++】类型限定符extern、const、Volatile、register

1、extern&#xff1a; 声明一个变量&#xff0c;extern声明的变量没有建立存储空间。 extern int a ; //变量在定义的时候创建存储空间。 ①当我们在编译器中试图运行以下代码&#xff0c;系统会报错。 错误原因是“无法解析外部符号_a”.系统认为变量a是没有开辟内存空间的…...

day54【代码随想录】二刷数组

文章目录前言一、二分查找&#xff08;力扣724&#xff09;二、移除元素&#xff08;力扣27&#xff09;【双指针】三、有序数组的平方&#xff08;力扣977&#xff09;【双指针】四、合并两个有序数组&#xff08;力扣88&#xff09;五、长度最小的子数组&#xff08;力扣209&…...

哪个品牌蓝牙耳机性价比高?性价比高的平价蓝牙耳机推荐

现如今&#xff0c;随着蓝牙技术的进步&#xff0c;蓝牙耳机在人们日常生活中的便捷性更胜从前。越来越多的蓝牙耳机品牌被大众看见、认可。那么&#xff0c;哪个品牌的蓝牙耳机性价比高&#xff1f;接下来&#xff0c;我给大家推荐几款性价比高的平价蓝牙耳机&#xff0c;一起…...

揭秘关于TFRcord的五脏六腑

揭秘关于TFRcord的五脏六腑 前言&#xff1a;本篇文章将演示如何创建、解析和使用tf.Example消息&#xff0c;以及如何在.tfrecord文件之间对tf.Example消息进行序列化、写入和读取。 教程讲解使用的都是结构化数据&#xff0c;文章最后还会演示如果将图片写成.tfrecord文件&am…...

【Shell学习笔记】3.Shell 传递参数及数组

前言 本章介绍Shell的传递参数和数组。 Shell 传递参数 我们可以在执行 Shell 脚本时&#xff0c;向脚本传递参数&#xff0c;脚本内获取参数的格式为&#xff1a;$n。n 代表一个数字&#xff0c;1 为执行脚本的第一个参数&#xff0c;2 为执行脚本的第二个参数&#xff0c;…...

【终结Bug】ModuleNotFoundError: No module named ‘cv2’

解决方案&#xff1a; 打开 cmd键入 pip install opencv_python -i https://pypi.tuna.tsinghua.edu.cn/simple...

SQL Server2008详细安装步骤(保姆式教程)

安装包下载 链接&#xff1a;https://pan.baidu.com/s/1Rjx4DHJBeCW2asC_4Kzo6Q?pwdchui 提取码&#xff1a;chui 安装过程 1.解压后使用管理员身份打开安装程序 2.选择全新安装或向现有安装添加新功能 3.确认 4.输入产品密钥&#xff08;上方网盘安装包里有&#xff0…...

Linux常用操作

Linux常用操作 前言常用命令&#xff1a;一些操作命令&#xff1a;前言 本文是笔者在使用cadence的过程中&#xff0c;操作linux的笔记&#xff0c;仅记录个人常用&#xff0c;持续更新 常用命令&#xff1a; &#xff08;1&#xff09;高频&#xff1a;会了这几个就能在文件…...

Golang 处理parquet文件实战教程

Parquet是Apache基金会支持的项目&#xff0c;是面向列存储二进制文件格式。支持不同类型的压缩方式&#xff0c;广泛用于数据科学和大数据环境&#xff0c;如Hadoop生态。 本文主要介绍Go如何生成和处理parquet文件。 创建结构体 首先创建struct&#xff0c;用于表示要处理…...

腾讯TIM实现即时通信 v3+ts实践

目录 初始化sdk 功能描述 初始化 准备 SDKAppID 调用初始化接口 监听事件 发送消息 创建消息 创建文本消息 登录登出 功能描述 登录 登出 销毁 登录设置 获取会话列表 功能描述 获取会话列表 获取全量的会话列表 历史消息 功能描述 拉取消息列表 分页拉取…...

华为OD机试 - 回文字符串(Java JS Python)

题目描述 如果一个字符串正读和反渎都一样(大小写敏感),则称它为一个「回文串」,例如: leVel是一个「回文串」,因为它的正读和反读都是leVel;同理a也是「回文串」art不是一个「回文串」,因为它的反读tra与正读不同Level不是一个「回文串」,因为它的反读leveL与正读不…...

APP测试的7大注意点。

1. 运行 1&#xff09; App安装完成后的试运行&#xff0c;可正常打开软件。 2&#xff09; App打开测试&#xff0c;是否有加载状态进度提示。 3&#xff09; App⻚面间的切换是否流畅&#xff0c;逻辑是否正确。 4&#xff09; 注册 同表单编辑⻚面 用户名密码⻓度 …...

设计模式-第4章(装饰模式)

装饰模式装饰模型装饰模式示例商场收银程序&#xff08;简单工厂策略装饰模式实现&#xff09;装饰模式总结装饰模型 装饰模式&#xff08;Decorator&#xff09;&#xff0c;动态地给一个对象添加一些额外的职责&#xff0c;就增加功能来说&#xff0c;装饰模式比生成子类更为…...

【算法设计-分治】快速幂与龟速乘

文章目录1. 快速幂2. 龟速乘3. 快速幂取模4. 龟速乘取模5. 快速幂取模优化1. 快速幂 算法原理&#xff1a; 计算 311&#xff1a; 311 (35)2 x 335 (32)2 x 332 3 x 3仅需计算 3 次&#xff0c;而非 11 次 计算 310&#xff1a; 310 (35)235 (32)2 x 332 3 x 3仅需计算…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...