Vue-Router源码实现详解
1.Hash模式
- hash就是url中#后面的部分
- hash改变时,页面不会从新加载,会触发hashchange事件,去监听hash改变,而且也会被记录到浏览器历史记录中
- vue-router的hash模式,主要是通过hashchange事件,根据hash值找到对应的组件去进行渲染(源码里会先判断浏览器支不支持popstate事件,如果支持,则是通过监听popstate事件,如果不支持,则监听hashchange事件)
hash模式页面跳转不刷新
根据http协议所示,url中hash改变请求是不会发送到服务端的,不管怎么location跳转,或者url上直接加上hash值回车,他都一样,请求是不会发送到服务端。
但是我们的系统里又引入了vue-router,其中hashchange这个事件监听到了hash的变化,从而触发了组件的更新,也就绘制出了相应的页面
2.History模式
- 通过history.pushstate去修改页面的地址
- 当history改变时,会触发popstate事件,所以可以通过监听popstate事件获取路由地址
- 根据当前路由地址找到对应组件渲染
history模式,切换路由时页面刷新
看一下正确的history模式下,首页刷新到显示的整体流程:
1.将这个完整的url发到服务器nginx2.ngix需要配置用这个uri在指给前端index.html(因为根本没有任何一个服务器提供了这个url路由,如果直接访问的话就是404,所以就要指回给前端,让前端自己去根据path来显示)
location / {root /usr/share/nginx/html/store;//项目存放的地址index index.html index.htm;try_files $uri $uri/ /index.html;//history模式下,需要配置它
}所以try_files $uri $uri/的意思就是,比如http://test.com/example先去查找单个文件example,如果example不存在,则去查找同名的文件目录/example/,如果再不存在,将进行重定向index.html(只有最后一个参数可以引起一个内部重定向)凡是404的路由,都会被重定向到index.html,这样就显示正确了3.此时nginx将这个请求指回了前端的index.html,index.html中开始加载js,js中已有vue-router的代码,vue-router自动触发了popstate这个事件,在这个事件回调中,绘制了这个path下对应的页面组件
3.实现vue-router
VueRouter需要做以下这些事情
- 实现VueRouter根据不同模式进行不同处理
- 根据传入的路由配置,生成对应的路由映射
- init函数监听hashchange或
popState事件,浏览器记录改变时重新渲染router-view组件 - 实现install静态方法
- 给Vue实例挂载router实例
- 注册全局组件和,
router-view组件通过当前url找到对应组件进行渲染,并且url改变时,重新渲染组件,router-link则渲染为a标签 - 使用
Object.defineProperty在Vue的原型上定义$router和$route属性
代码实现
首先在创建文件 router/index.js,router/my-router.js
在index中我就不做多讲解了,和平常vue-router一样的配置,只是不需要vue-router,我们自己实现
一下都会有详细的注释,每一项的作用
import Vue from 'vue'
import VueRouter from './my-router'; //实现router文件
import HomeView from '../views/HomeView.vue' //home文件
import about from '../views/AboutView.vue' //about文件Vue.use(VueRouter) //注意! 这是我们自己实现的文件,只是名字叫vuerouterconst routes = [ //这是我们的路由表{path: '/home',name: 'home',component: HomeView},{path: '/about',name: 'about',// route level code-splitting// this generates a separate chunk (about.[hash].js) for this route// which is lazy-loaded when the route is visited.component: about}
]const router = new VueRouter({ //创建实例mode: 'history', //模式 hash historyroutes //路由表给实例传过去
})export default router //最后到处router
接下来就是正式实现router
首先在my-router最顶部,声明一个变量
Vue并将其初始化为null
//my-router.js
let Vue = null; //保存Vue的构造函数,在插件中要使用,保留在将来为Vue分配一个值,减少全局命名空间的污染
定义一个HistoryRoute的类,并在其构造函数中将
this.current也初始化为null用途:
- 状态管理:
this.current用于存储当前路由的状态或信息,比如当前激活的路由路径、参数等。- 初始化:通过将其初始化为
null,你可以在类的其他方法中根据需要更新this.current的值,而不会受到未初始化属性的影响。- 灵活性:将
this.current初始化为null提供了灵活性,允许你在类的生命周期中的任何时刻为其分配一个具体的值。
//my-router.js
let Vue = null; //保存Vue的构造函数,在插件中要使用,保留在将来为Vue分配一个值,减少全局命名空间的污染
class HistoryRoute {constructor() {this.current = null;}
}
定义一个HistoryRoute的类
构造函数:
constructor(options):接收一个options对象,该对象包含两个属性:mode和routes。mode指定路由模式(hash或history),routes是一个路由配置数组,每个路由配置对象包含path和component属性。this.changeMap:将routes数组转换为一个对象(Map),以path为键,component为值,方便后续根据路径快速查找对应的组件。Vue.util.defineReactive(this, "history", new HistoryRoute());和this.history = new HistoryRoute();:这里设置了history属性,后者覆盖了前者。HistoryRoute类用于管理当前路由状态。
class VueRouter {// 可以看到,暂时传入了两个,一个是mode,还有一个是routes数组。因此,我们可以这样实现构造器constructor(options) {this.mode = options.mode || "hash"; //默认是hashthis.routes = options.routes || []; //默认为空// 由于直接处理数组比较不方便,所以我们做一次转换,采用path为key,component为value的方式this.routesMap = this.changeMap(this.routes);// 我们还需要在vue-router的实例中保存当前路径(在包含一些例如params信息,其实就是$route),所以我们为了方便管理,使用一个对象来表示:Vue.util.defineReactive(this, "history", new HistoryRoute());this.history = new HistoryRoute();}changeMap(routes) {// 使用render函数我们可以用js语言来构建DOMreturn routes.reduce((pre, next) => {console.log(pre);pre[next.path] = next.component;console.log(pre);return pre;}, {});}
}
添加init 方法:
- 根据
mode的不同,为window添加相应的事件监听器,以监听路由变化(hashchange或popstate事件),并更新history.current属性。- 在页面加载时(
load事件),也根据当前URL设置history.current。
class VueRouter {// 可以看到,暂时传入了两个,一个是mode,还有一个是routes数组。因此,我们可以这样实现构造器constructor(options) {this.mode = options.mode || "hash";this.routes = options.routes || [];// 由于直接处理数组比较不方便,所以我们做一次转换,采用path为key,component为value的方式this.routesMap = this.changeMap(this.routes);// 我们还需要在vue-router的实例中保存当前路径(在包含一些例如params信息,其实就是$route),所以我们为了方便管理,使用一个对象来表示:Vue.util.defineReactive(this, "history", new HistoryRoute());this.history = new HistoryRoute();this.init();}init() {// 如果是hash模式if (this.mode === "hash") {location.hash ? void 0 : (location.hash = "/");window.addEventListener("load", () => {this.history.current = location.hash.slice(1);});window.addEventListener("hashchange", () => {console.log(location.hash.slice(1))this.history.current = location.hash.slice(1);});}// 如果是history模式if (this.mode === "history") {location.pathname ? void 0 : (location.pathname = "/");window.addEventListener("load", () => {console.log(location.pathname)this.history.current = location.pathname;});window.addEventListener("popstate", () => {console.log(location.pathname)this.history.current = location.pathname;});}}changeMap(routes) {// 使用render函数我们可以用js语言来构建DOMreturn routes.reduce((pre, next) => {console.log(pre);pre[next.path] = next.component;console.log(pre);return pre;}, {});}
Vue Router作为一个Vue插件,需要创建instll方法
- 设置Vue实例:
Vue = v;:将传入的Vue实例赋值给全局变量Vue,以便后续使用。- 全局混入:
- 使用
Vue.mixin在Vue的生命周期钩子beforeCreate中注入代码,用于处理路由相关的初始化。- 在根组件中,保存
_router和_root属性,分别指向VueRouter实例和根组件自身。- 对于非根组件,通过
$parent找到根组件,从而访问到_router和_root。
VueRouter.install = (v) => {Vue = v;// vue-router还自带了两个组件,分别是router-link和router-view 在Vue.use(VueRouter)的时候加载的 所以我们要写在install里面// 新增代码Vue.mixin({beforeCreate() {// 如果是根组件if (this.$options && this.$options.router) {console.log(this.$options)// 将根组件挂载到_root上this._root = this;this._router = this.$options.router;// 拦截router-linkthis._router.mode === "history" && document.addEventListener("click", (e) => {if (e.target.className === "router-link-to") {// 阻止默认跳转事件e.preventDefault();// 手动改变url路径console.log(e.target.getAttribute("href"))history.pushState(null, "", e.target.getAttribute("href"));// 为current赋值url路径this._router.history.current = location.pathname;}});} else {// 如果是子组件// 将根组件挂载到子组件的_root上this._root = this.$parent && this.$parent._root;console.log(this._root);}},});
};
- 使用
Object.defineProperty在Vue的原型上定义$router和$route属性,以便在任何Vue组件中通过this.$router和this.$route访问到VueRouter实例和当前路由信息。- 在install方法里面写就行
// 定义$routerObject.defineProperty(Vue.prototype, "$router", {get() {console.log(this);return this._root._router;},});// 定义$routeObject.defineProperty(Vue.prototype, "$route", {get() {return this._root._router.history.current;},});
定义全局组件(install方法里面添加)
router-link:一个用于导航的<a>标签组件,根据路由模式(hash或history)自动添加#或正常路径。点击时,如果是history模式,会阻止默认跳转行为,改为手动更新URL和路由状态。router-view:一个用于渲染当前路由对应组件的占位符组件。它根据history.current和routesMap找到对应的组件,并使用Vue的render函数渲染。
Vue.component("router-link", {props: {to: String,},render(h) {const mode = this._root._router.mode;let to = mode === "hash" ? "#" + this.to : this.to;return h("a",{attrs: {href: to,},// 新增代码class: "router-link-to",},this.$slots.default);},});Vue.component("router-view", {render(h) {const current = this._root._router.history.current;const routesMap = this._root._router.routesMap;return h(routesMap[current]);},});
完整代码
/** @Author: hukai huzhengen@gmail.com* @Date: 2023-06-08 17:49:08* @LastEditors: hukai huzhengen@gmail.com* @LastEditTime: 2024-10-24 11:08:00* @FilePath: \vue源码\vue-router-yuanma\src\router\my-router.js* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE*/
let Vue = null;
class HistoryRoute {constructor() {this.current = null;}
}
// 因为router时new出来的 并且穿了一个对象 配置的路由由此可知router是一个class类
class VueRouter {// 可以看到,暂时传入了两个,一个是mode,还有一个是routes数组。因此,我们可以这样实现构造器constructor(options) {this.mode = options.mode || "hash";this.routes = options.routes || [];// 由于直接处理数组比较不方便,所以我们做一次转换,采用path为key,component为value的方式this.routesMap = this.changeMap(this.routes);// 我们还需要在vue-router的实例中保存当前路径(在包含一些例如params信息,其实就是$route),所以我们为了方便管理,使用一个对象来表示:Vue.util.defineReactive(this, "history", new HistoryRoute());this.history = new HistoryRoute();this.init();}init() {// 如果是hash模式if (this.mode === "hash") {location.hash ? void 0 : (location.hash = "/");window.addEventListener("load", () => {this.history.current = location.hash.slice(1);});window.addEventListener("hashchange", () => {console.log(location.hash.slice(1))this.history.current = location.hash.slice(1);});}// 如果是history模式if (this.mode === "history") {location.pathname ? void 0 : (location.pathname = "/");window.addEventListener("load", () => {console.log(location.pathname)this.history.current = location.pathname;});window.addEventListener("popstate", () => {console.log(location.pathname)this.history.current = location.pathname;});}}changeMap(routes) {// 使用render函数我们可以用js语言来构建DOMreturn routes.reduce((pre, next) => {console.log(pre);pre[next.path] = next.component;console.log(pre);return pre;}, {});}
}// 通过Vue.use 知道里面有一个install方法 并且第一个参数是Vue实例
VueRouter.install = (v) => {Vue = v;// vue-router还自带了两个组件,分别是router-link和router-view 在Vue.use(VueRouter)的时候加载的 所以我们要写在install里面// 新增代码Vue.mixin({beforeCreate() {// 如果是根组件if (this.$options && this.$options.router) {console.log(this.$options)// 将根组件挂载到_root上this._root = this;this._router = this.$options.router;// 拦截router-linkthis._router.mode === "history" && document.addEventListener("click", (e) => {if (e.target.className === "router-link-to") {// 阻止默认跳转事件e.preventDefault();// 手动改变url路径console.log(e.target.getAttribute("href"))history.pushState(null, "", e.target.getAttribute("href"));// 为current赋值url路径this._router.history.current = location.pathname;}});} else {// 如果是子组件// 将根组件挂载到子组件的_root上this._root = this.$parent && this.$parent._root;console.log(this._root);}},});// 定义$routerObject.defineProperty(Vue.prototype, "$router", {get() {console.log(this);return this._root._router;},});// 定义$routeObject.defineProperty(Vue.prototype, "$route", {get() {return this._root._router.history.current;},});Vue.component("router-link", {props: {to: String,},render(h) {const mode = this._root._router.mode;let to = mode === "hash" ? "#" + this.to : this.to;return h("a",{attrs: {href: to,},// 新增代码class: "router-link-to",},this.$slots.default);},});Vue.component("router-view", {render(h) {const current = this._root._router.history.current;const routesMap = this._root._router.routesMap;return h(routesMap[current]);},});
};
export default VueRouter;
最后在main.js中注册一下就行
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'Vue.config.productionTip = false
new Vue({router,store,render: h => h(App)
}).$mount('#app')
APP.vue中
<template><div id="app"><nav><router-link to="/home">Home</router-link> |<router-link to="/about">About</router-link></nav><router-view/></div>
</template>
<script>
export default {name:"Router",mounted(){console.log(this.$router)}
}
</script>
<style>
#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;
}nav {padding: 30px;
}nav a {font-weight: bold;color: #2c3e50;
}nav a.router-link-exact-active {color: #42b983;
}
</style>
总结
这段代码通过定义VueRouter类和install方法,实现了一个简化版的Vue Router。它允许开发者定义路由规则,并在Vue应用中通过和组件实现页面导航和组件渲染。尽管这个实现相对简单,但它展示了Vue Router的核心概念和工作原理。
相关文章:
Vue-Router源码实现详解
1.Hash模式 hash就是url中#后面的部分hash改变时,页面不会从新加载,会触发hashchange事件,去监听hash改变,而且也会被记录到浏览器历史记录中vue-router的hash模式,主要是通过hashchange事件,根据hash值找…...
程序员节日的日期是10月24日程序员日
程序员节日的日期是10月24日。 这一天被称为中国程序员日或1024程序员节,由博客园、CSDN等自发组织设立,旨在纪念程序员对科技世界的贡献。 程序员节日的由来和意义 1024程序员节的由来可以追溯到2010年,最初由网友提出设立一个…...
联邦学习中的数据异构性
在联邦学习(Federated Learning, FL)领域中, 异构数据(Heterogeneous Data) 是指不同客户端所持有的本地数据在特征分布、类别分布、数量等方面存在差异的数据。这种数据的异质性是联邦学习面临的一大挑战,…...
Python小程序 - 替换文件内容
1. 写入文件c:\a.txt 1)共写入10行 2)每行内容 0123456789 # 1 ls 0123456789 ln 10 with open("c:/a.txt", w,encodingUTF-8) as f:for i in range(ln):f.write(ls\n)######################################### 2 ln 10…...
k8s备份恢复(velero)
velero简介 velero官网: https://velero.io/ velero-github: https://github.com/vmware-tanzu/velero velero的特性 备份可以按集群资源的子集,按命名空间、资源类型标签选择器进行过滤,从而为备份和恢复的内容提供高度的灵活…...
LED户外屏:面对复杂环境的七大挑战
户外LED显示屏作为现代城市广告和信息传播的重要媒介,其应用范围越来越广泛。然而,与室内环境相比,户外环境的复杂多变对LED显示屏提出了更高的要求。本文将探讨户外LED显示屏在设计和应用过程中必须考虑的七个关键问题。 1. 高分辨率 户外LE…...
LabVIEW自动化流动返混实验系统
随着工业自动化的不断发展,连续流动反应器在化工、医药等领域中的应用日益广泛。传统的流动返混实验操作复杂,数据记录和处理不便,基于LabVIEW的全自动流动返混实验系统能自动测定多釜反应器、单釜反应器和管式反应器的停留时间分布ÿ…...
【性能优化】安卓性能优化之CPU优化
【性能优化】安卓性能优化之CPU优化 CPU优化及常用工具原理与文章参考常用ADB常用原理、监控手段原理监控手段多线程并发解决耗时UI相关 常见场景排查CPU占用过高常用系统/开源分析工具AndroidStudio ProfilerSystraceBtracePerfettoTraceView和 Profile ANR相关ANR原理及常见场…...
springboot二手图书交易系统-计算机设计毕业源码88413
目 录 摘要 1 绪论 1.1研究背景 1.2研究意义 1.3论文结构与章节安排 2 二手图书交易系统系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据流程 3.3.2 业务流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章小结 3 二手…...
解决ElasticSearch启动成功却无法在浏览器访问问题
目录 前言: 问题复现 : 解决问题: 1、修改sysctl.conf文件 2、在sysctl.conf文件增加这段东西 3、 然后保存退出,输入以下命令使其生效 结语: 前言: 这篇文章是小白我今天突然启动es,发现e…...
稀土有色包芯线良好的导电性
稀土有色包芯线是一种结合了稀土元素和有色金属(如铜、铝、镁等)的特殊线材。以下是对稀土有色包芯线的详细介绍: 一、组成与结构 芯线:由稀土元素和有色金属组成的合金制成。稀土元素(如镧、铈、镁等)的添加量在一定范围内,以确保合金性能的…...
SIP 业务举例之 Call Forwarding Unconditional(无条件呼转)
目录 1. Call Forwarding Unconditional 简介 2. RFC5359 的 Call Forwarding Unconditional 信令流程 PS:Dialog 建立条件 Dialog 会话完全建立 3. Call Forwarding Unconditional 过程总结 博主wx:yuanlai45_csdn 博主qq:2777137742 想要 深入学习 5GC IMS 等通信知识…...
基于stm32的esp8266的WIFI控制风扇实验
实验案例WIFI控制风扇 项目需求 电脑通过esp8266模块远程遥控风扇。 项目框图 风扇模块封装 #include "sys.h" #include "fan.h"void fan_init(void) {GPIO_InitTypeDef gpio_initstruct;//打开时钟…...
java中的ScheduledExecutorService介绍和使用案例
ScheduledExecutorService 是 Java 并发包 java.util.concurrent 中的一个接口,它提供了一种机制,允许我们安排一个任务在给定的延迟后运行,或者定期地执行。 主要特点 单次调度:可以安排任务在一定的延迟后执行一次。周期性调度…...
4天涨粉14万!这个AI小众赛道粉丝涨疯了吧?保姆级教程免费教会你!
测一下你的搞钱灵敏度有多高,看下面两张截图,有没有发现什么异常值? 发现了吧? 第一张是10月17号截的,第二张是21号,4天时间粉丝从2.8万飙到16.6万,涨粉14万! 这个号我几天之前就发…...
RK3588 技术分享 | 在Android系统中使用NPU实现Yolov5分类检测
随着人工智能和大数据时代的到来,传统嵌入式处理器中的CPU和GPU逐渐无法满足日益增长的深度学习需求。为了应对这一挑战,在一些高端处理器中,NPU(神经网络处理单元)也被集成到了处理器里。NPU的出现不仅减轻了CPU和GPU…...
itext 转换word文档转pdf
itext 转换word文档转pdf <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version><scope>compile</scope></dependency> <dependency><groupId>org.a…...
WSL-默认root登录
WSL-默认root登录 使用管理员,打开powershell PS C:\WINDOWS\system32> wsl -l 适用于 Linux 的 Windows 子系统分发版: Ubuntu-22.04 (默认) PS C:\WINDOWS\system32> ubuntu2204.exe config --default-user root PS C:\WINDOWS\system32>修改之后&…...
ASIO网络调试助手之四:浅谈QTcpServer性能
网络上有些质疑Qt Network模块性能的声音,本文将从理论和压测两个方面对比ASIO tcp server和QTcpServer在Windows上的性能,最后给出结论。 一.理论 QTcpServer在Windows上的实现是基于select模型,源码位置:5.12.6\Src\qtbase\src\network\socket\qnativesocketengine_win…...
快手 日常实习一面面经
官网投递,三天约面 线上面试 (60mins): 1. 自我介绍 2. 问实习 介绍自己做的业务上一段实习的公司框架和开源的 RPC 框架的区别负责的业务与核心业务的依赖关系场景题 -> 设计高并发下的负载均衡 3. 项目拷打 手写 双检锁…...
ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统
Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...
从零开始了解数据采集(二十八)——制造业数字孪生
近年来,我国的工业领域正经历一场前所未有的数字化变革,从“双碳目标”到工业互联网平台的推广,国家政策和市场需求共同推动了制造业的升级。在这场变革中,数字孪生技术成为备受关注的关键工具,它不仅让企业“看见”设…...
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅!
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅! 🌱 前言:一棵树的浪漫,从数组开始说起 程序员的世界里,数组是最常见的基本结构之一,几乎每种语言、每种算法都少不了它。可你有没有想过,一组看似“线性排列”的有序数组,竟然可以**“长”成一棵平衡的二…...
aurora与pcie的数据高速传输
设备:zynq7100; 开发环境:window; vivado版本:2021.1; 引言 之前在前面两章已经介绍了aurora读写DDR,xdma读写ddr实验。这次我们做一个大工程,pc通过pcie传输给fpga,fpga再通过aur…...
新版NANO下载烧录过程
一、序言 搭建 Jetson 系列产品烧录系统的环境需要在电脑主机上安装 Ubuntu 系统。此处使用 18.04 LTS。 二、环境搭建 1、安装库 $ sudo apt-get install qemu-user-static$ sudo apt-get install python 搭建环境的过程需要这个应用库来将某些 NVIDIA 软件组件安装到 Je…...
Oracle实用参考(13)——Oracle for Linux物理DG环境搭建(2)
13.2. Oracle for Linux物理DG环境搭建 Oracle 数据库的DataGuard技术方案,业界也称为DG,其在数据库高可用、容灾及负载分离等方面,都有着非常广泛的应用,对此,前面相关章节已做过较为详尽的讲解,此处不再赘述。 需要说明的是, DG方案又分为物理DG和逻辑DG,两者的搭建…...
