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脚手架多页自动化生成实践
前言 在前端开发过程中,常常面对多种业务场景。到目前为止,前端对于不同场景的处理通常会采用不同的渲染方案来组合处理,常见的渲染方案包括:CSR(Client Side Rendering)、SSR(Server Side Rendering)、SSG(Static Site Generati…...
【SQL语句优化】
SQL语句优化是提高数据库查询性能的重要手段之一,下面是几种常见的SQL语句优化方法和案例: 减少查询的数据量 减少查询的数据量:使用 WHERE 子句和索引来限制检索行数,只检索需要的行,避免检索全部行数据。 例子&am…...
阿里P8:做测试10年我的一些经验分享,希望你们少走弯路
我是在2015年毕业的,当时是读的普通本科,不上不下的专业水平,毕业的时候,恰好遇到了金融危机。校园招聘里阴差阳错的巧合,让我走上了软件测试工程师的道路。 入职第一天,来了个高大上的讲师,记…...
栈在括号匹配中的应用(栈/链栈 纯C实现)
目录 1 问题背景 2 具体思路 3 代码实现 3.1 顺序栈实现 3.2 链栈实现 1 问题背景 栈的括号匹配问题是指在给定一个字符串(包含多种括号),判断其中的括号是否能够正确匹配,即每个左括号是否有一个对应的右括号与之匹配&#x…...
C语言Switch语句用法
C switch 语句 一个 switch 语句允许测试一个变量等于多个值时的情况。每个值称为一个 case,且被测试的变量会对每个 switch case 进行检查。 语法 C 语言中 switch 语句的语法: switch(expression){case constant-expression :statement(s);break;…...
Curl编码请求参数,API接口请求示例参数
请求参数请求参数:num_iid610947572360 参数说明: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: 声明一个变量,extern声明的变量没有建立存储空间。 extern int a ; //变量在定义的时候创建存储空间。 ①当我们在编译器中试图运行以下代码,系统会报错。 错误原因是“无法解析外部符号_a”.系统认为变量a是没有开辟内存空间的…...
day54【代码随想录】二刷数组
文章目录前言一、二分查找(力扣724)二、移除元素(力扣27)【双指针】三、有序数组的平方(力扣977)【双指针】四、合并两个有序数组(力扣88)五、长度最小的子数组(力扣209&…...
哪个品牌蓝牙耳机性价比高?性价比高的平价蓝牙耳机推荐
现如今,随着蓝牙技术的进步,蓝牙耳机在人们日常生活中的便捷性更胜从前。越来越多的蓝牙耳机品牌被大众看见、认可。那么,哪个品牌的蓝牙耳机性价比高?接下来,我给大家推荐几款性价比高的平价蓝牙耳机,一起…...
揭秘关于TFRcord的五脏六腑
揭秘关于TFRcord的五脏六腑 前言:本篇文章将演示如何创建、解析和使用tf.Example消息,以及如何在.tfrecord文件之间对tf.Example消息进行序列化、写入和读取。 教程讲解使用的都是结构化数据,文章最后还会演示如果将图片写成.tfrecord文件&am…...
【Shell学习笔记】3.Shell 传递参数及数组
前言 本章介绍Shell的传递参数和数组。 Shell 传递参数 我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,…...
【终结Bug】ModuleNotFoundError: No module named ‘cv2’
解决方案: 打开 cmd键入 pip install opencv_python -i https://pypi.tuna.tsinghua.edu.cn/simple...
SQL Server2008详细安装步骤(保姆式教程)
安装包下载 链接:https://pan.baidu.com/s/1Rjx4DHJBeCW2asC_4Kzo6Q?pwdchui 提取码:chui 安装过程 1.解压后使用管理员身份打开安装程序 2.选择全新安装或向现有安装添加新功能 3.确认 4.输入产品密钥(上方网盘安装包里有࿰…...
Linux常用操作
Linux常用操作 前言常用命令:一些操作命令:前言 本文是笔者在使用cadence的过程中,操作linux的笔记,仅记录个人常用,持续更新 常用命令: (1)高频:会了这几个就能在文件…...
Golang 处理parquet文件实战教程
Parquet是Apache基金会支持的项目,是面向列存储二进制文件格式。支持不同类型的压缩方式,广泛用于数据科学和大数据环境,如Hadoop生态。 本文主要介绍Go如何生成和处理parquet文件。 创建结构体 首先创建struct,用于表示要处理…...
腾讯TIM实现即时通信 v3+ts实践
目录 初始化sdk 功能描述 初始化 准备 SDKAppID 调用初始化接口 监听事件 发送消息 创建消息 创建文本消息 登录登出 功能描述 登录 登出 销毁 登录设置 获取会话列表 功能描述 获取会话列表 获取全量的会话列表 历史消息 功能描述 拉取消息列表 分页拉取…...
华为OD机试 - 回文字符串(Java JS Python)
题目描述 如果一个字符串正读和反渎都一样(大小写敏感),则称它为一个「回文串」,例如: leVel是一个「回文串」,因为它的正读和反读都是leVel;同理a也是「回文串」art不是一个「回文串」,因为它的反读tra与正读不同Level不是一个「回文串」,因为它的反读leveL与正读不…...
APP测试的7大注意点。
1. 运行 1) App安装完成后的试运行,可正常打开软件。 2) App打开测试,是否有加载状态进度提示。 3) App⻚面间的切换是否流畅,逻辑是否正确。 4) 注册 同表单编辑⻚面 用户名密码⻓度 …...
设计模式-第4章(装饰模式)
装饰模式装饰模型装饰模式示例商场收银程序(简单工厂策略装饰模式实现)装饰模式总结装饰模型 装饰模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为…...
【算法设计-分治】快速幂与龟速乘
文章目录1. 快速幂2. 龟速乘3. 快速幂取模4. 龟速乘取模5. 快速幂取模优化1. 快速幂 算法原理: 计算 311: 311 (35)2 x 335 (32)2 x 332 3 x 3仅需计算 3 次,而非 11 次 计算 310: 310 (35)235 (32)2 x 332 3 x 3仅需计算…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
