[{"data":1,"prerenderedAt":326},["ShallowReactive",2],{"content-query-ZCOhJLIDMf":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"cover":11,"type":12,"category":13,"body":14,"_type":320,"_id":321,"_source":322,"_file":323,"_stem":324,"_extension":325},"/technology-blogs/zh/2020","zh",false,"","项目分享 | MindSpore Insight AI可视化工具开发心得","MindSpore Insight是昇思MindSpore的可视化调试调优工具。作为开发者，我参与了MindSpore Insight工具部分功能的开发。本文将对MindSpore Insight进行简要介绍、其次介绍在开发中所用开发框架Vue的响应式原理。","2022-12-20","https://obs-mindspore-file.obs.cn-north-4.myhuaweicloud.com/file/2022/12/27/9243f5fcec2941d0b51d39dfeb2b3c92.png","technology-blogs","实践",{"type":15,"children":16,"toc":317},"root",[17,25,49,57,65,69,77,85,99,108,116,124,129,136,141,146,151,156,164,169,179,184,192,199,211,216,221,231,246,257,262,270,275,283,288,296,304,312],{"type":18,"tag":19,"props":20,"children":22},"element","h1",{"id":21},"项目分享-mindspore-insight-ai可视化工具开发心得",[23],{"type":24,"value":8},"text",{"type":18,"tag":26,"props":27,"children":28},"p",{},[29,35,37,42,44],{"type":18,"tag":30,"props":31,"children":32},"strong",{},[33],{"type":24,"value":34},"作者",{"type":24,"value":36},"：",{"type":18,"tag":30,"props":38,"children":39},{},[40],{"type":24,"value":41},"张玉涛",{"type":24,"value":43}," ｜**学校：**",{"type":18,"tag":30,"props":45,"children":46},{},[47],{"type":24,"value":48},"郑州大学",{"type":18,"tag":26,"props":50,"children":51},{},[52],{"type":18,"tag":30,"props":53,"children":54},{},[55],{"type":24,"value":56},"01",{"type":18,"tag":26,"props":58,"children":59},{},[60],{"type":18,"tag":30,"props":61,"children":62},{},[63],{"type":24,"value":64},"概述",{"type":18,"tag":26,"props":66,"children":67},{},[68],{"type":24,"value":9},{"type":18,"tag":26,"props":70,"children":71},{},[72],{"type":18,"tag":30,"props":73,"children":74},{},[75],{"type":24,"value":76},"02",{"type":18,"tag":26,"props":78,"children":79},{},[80],{"type":18,"tag":30,"props":81,"children":82},{},[83],{"type":24,"value":84},"MindSpore Insight****介绍",{"type":18,"tag":26,"props":86,"children":87},{},[88,90],{"type":24,"value":89},"MindSpore Insight为昇思MindSpore提供了简单易用的调优调试能力。在训练过程中，可以将标量、张量、图像、计算图、模型超参、训练耗时等数据记录到文件中，通过MindSpore Insight可视化页面进行查看及分析。MindSpore Insight的宏观上的架构如下图所示。其中Summary log是使用昇思MindSpore训练的模型日志，通过python进行对日志进行解码，将数据处理为json格式的API，前端通过请求该API进行相应功能的展示。详细的介绍可查看官网（",{"type":18,"tag":91,"props":92,"children":96},"a",{"href":93,"rel":94},"https://www.mindspore.cn/mindinsight/docs/zh-CN/master/index.html%EF%BC%89%E6%88%96%E7%82%B9%E5%87%BB%E4%B8%8B%E6%96%B9**%E2%80%9C%E9%98%85%E8%AF%BB%E5%8E%9F%E6%96%87%E2%80%9D**%E3%80%82",[95],"nofollow",[97],{"type":24,"value":98},"https://www.mindspore.cn/mindinsight/docs/zh-CN/master/index.html）或点击下方**“阅读原文”**。",{"type":18,"tag":26,"props":100,"children":101},{},[102],{"type":18,"tag":103,"props":104,"children":107},"img",{"alt":105,"src":106},"%E5%9B%BE%E7%89%87.png","https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbs/e64/154/b38/90a1d5d431e64154b387b3660e356ff5.20221227030356.20741682252370077659660305372348:50531226030817:2400:298DCBE14BEDD04959176DA3F8FE31844716B936853F91A81B60E551FC4ECE29.png",[],{"type":18,"tag":26,"props":109,"children":110},{},[111],{"type":18,"tag":30,"props":112,"children":113},{},[114],{"type":24,"value":115},"03",{"type":18,"tag":26,"props":117,"children":118},{},[119],{"type":18,"tag":30,"props":120,"children":121},{},[122],{"type":24,"value":123},"浅谈Vue2响应式原理",{"type":18,"tag":26,"props":125,"children":126},{},[127],{"type":24,"value":128},"Vue是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建，并提供了一套声明式的、组件化的编程模型，帮助你高效地开发用户界面。它的核心功能有声明式渲染和响应式，Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM。如下图所示，当data属性中的数据发生变化时，其视图就会更新，不需要用户操作DOM就可让视图更新。那么Vue是如何知道更新的是这些数据呢？这就涉及到Vue响应式的实现原理。",{"type":18,"tag":26,"props":130,"children":131},{},[132],{"type":18,"tag":103,"props":133,"children":135},{"alt":105,"src":134},"https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbs/e64/154/b38/90a1d5d431e64154b387b3660e356ff5.20221227030426.91454411114598768604894253675047:50531226030817:2400:51EEC5AA31568743721BE9E7696BEA85DFB98602CC8436B561B81A0AB440B4F7.png",[],{"type":18,"tag":26,"props":137,"children":138},{},[139],{"type":24,"value":140},"Vue的响应式是指以数据驱动视图的，也就是数据发生变化，会重新渲染页面。该过程实现要考虑的有以下三点：",{"type":18,"tag":26,"props":142,"children":143},{},[144],{"type":24,"value":145},"● 数据劫持：追踪数据的变化",{"type":18,"tag":26,"props":147,"children":148},{},[149],{"type":24,"value":150},"● 依赖收集：收集视图依赖的数据",{"type":18,"tag":26,"props":152,"children":153},{},[154],{"type":24,"value":155},"● 通知视图：数据变化时，通知视图部分更新",{"type":18,"tag":26,"props":157,"children":158},{},[159],{"type":18,"tag":30,"props":160,"children":161},{},[162],{"type":24,"value":163},"3.1 数据劫持和依赖收集",{"type":18,"tag":26,"props":165,"children":166},{},[167],{"type":24,"value":168},"**数据劫持：**指的是在访问或者修改对象的某个属性时，通过一段代码拦截这个行为，进行额外的操作或者修改返回结果。在下面的代码很好地诠释了数据劫持。当给obj中的name属性赋值的时候，会触发defineProperty方法中的set函数，而访问obj中的name属性值的时候会触发get函数，在这里set函数和get函数就拦截了访问和修改对象属性的这个行为，在get中和set中可以写上额外的操作，这就是数据劫持。",{"type":18,"tag":170,"props":171,"children":173},"pre",{"code":172},"/*\n  Object.defineProperty(obj,prop,descriptor)\n    obj 要定义属性的对象\n    prop 要定义或修改的属性的名称\n    descriptor 要定义或修改的属性描述符\n  输出结果：\n      数据劫持 set name : 张三\n      数据劫持 get name : 张三\n      张三\n*/\nconst obj={}\nObject.defineProperty(obj,\"name\",{\n        get(){\n            console.log(\"数据劫持 get name :\",nameVal);\n            return nameVal;\n        },\n        set(newVal){\n            console.log(\"数据劫持 set name :\",newVal)\n            nameVal = newVal\n        }\n});\nobj.name=\"张三\";\nconsole.log(obj.name)\n",[174],{"type":18,"tag":175,"props":176,"children":177},"code",{"__ignoreMap":7},[178],{"type":24,"value":172},{"type":18,"tag":26,"props":180,"children":181},{},[182],{"type":24,"value":183},"**依赖收集：**视图中用到了哪个数据，视图就依赖哪个数据，把变化的数据收集起来，这个过程就是依赖收集。",{"type":18,"tag":26,"props":185,"children":186},{},[187],{"type":18,"tag":30,"props":188,"children":189},{},[190],{"type":24,"value":191},"3.2****实现原理介绍",{"type":18,"tag":26,"props":193,"children":194},{},[195],{"type":18,"tag":103,"props":196,"children":198},{"alt":105,"src":197},"https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbs/e64/154/b38/90a1d5d431e64154b387b3660e356ff5.20221227030517.56238943049457766298444586177252:50531226030817:2400:FDC6D11861D6ACB7F1657B1C25ED694C4B7F872605A45C7B91794AB1055158AB.png",[],{"type":18,"tag":26,"props":200,"children":201},{},[202,204],{"type":24,"value":203},"上图(参考：",{"type":18,"tag":91,"props":205,"children":208},{"href":206,"rel":207},"https://blog.csdn.net/Mikon%5C_0703/article/details/111367773)%E6%98%AFVue%E5%AE%9E%E7%8E%B0%E5%93%8D%E5%BA%94%E5%BC%8F%E5%8E%9F%E7%90%86%E7%9A%84%E6%B5%81%E7%A8%8B%E5%9B%BE%EF%BC%8C%E4%B8%BB%E8%A6%81%E6%9C%89%E4%B8%89%E4%B8%AA%E9%87%8D%E8%A6%81%E9%83%A8%E5%88%86%EF%BC%9A",[95],[209],{"type":24,"value":210},"https://blog.csdn.net/Mikon\\_0703/article/details/111367773)是Vue实现响应式原理的流程图，主要有三个重要部分：",{"type":18,"tag":26,"props":212,"children":213},{},[214],{"type":24,"value":215},"**监听器(Observer)：**对数据对象进行遍历，利用Object.defineProperty()给属性都加上 setter和getter。这样的话，给这个对象的某个值赋值，就会触发setter，那么就能监听到了数据变化。",{"type":18,"tag":26,"props":217,"children":218},{},[219],{"type":24,"value":220},"**事件中心(Dep )：**用来收集依赖和通知订阅者。如果属性发生变化，需要通知订阅者Watcher，看是否需要更新。因为订阅者有多个，所以需要一个消息订阅器（发布者）Dep（订阅者集合的管理数组）来专门收集这些订阅者，在Observer和Watcher之间进行统一管理。每个Observer实例都有一个Dep实例。",{"type":18,"tag":26,"props":222,"children":223},{},[224,229],{"type":18,"tag":30,"props":225,"children":226},{},[227],{"type":24,"value":228},"订阅者(Watcher):",{"type":24,"value":230}," 将View的相关指令初始化为一个订阅者Watcher，并替换模板数据或绑定相应的函数，此时当订阅者Watcher接收到相应属性的变化，就会执行对应的更新函数，从而更新视图。",{"type":18,"tag":26,"props":232,"children":233},{},[234,239,241],{"type":18,"tag":30,"props":235,"children":236},{},[237],{"type":24,"value":238},"3.3",{"type":24,"value":240}," ",{"type":18,"tag":30,"props":242,"children":243},{},[244],{"type":24,"value":245},"Vue源码中的体现",{"type":18,"tag":26,"props":247,"children":248},{},[249,251],{"type":24,"value":250},"Vue2中响应式实现的源码链接：",{"type":18,"tag":91,"props":252,"children":255},{"href":253,"rel":254},"https://github.com/vuejs/vue/blob/v2.6.14/src/core/observer",[95],[256],{"type":24,"value":253},{"type":18,"tag":26,"props":258,"children":259},{},[260],{"type":24,"value":261},"监听器(Observer)的实现",{"type":18,"tag":170,"props":263,"children":265},{"code":264},"//https://github.com/vuejs/vue/blob/v2.6.14/src/core/observer/index.js\nwalk (obj: Object) {\n    const keys = Object.keys(obj) // 遍历数据对象利用 \n    Object.defineProperty()给属性都加上 setter和getter。\n    for (let i = 0; i \u003C keys.length; i++) {\n      defineReactive(obj, keys[i])\n    }\n  }\n export function defineReactive (\n  obj: Object,\n  key: string,\n  val: any,\n  customSetter?: ?Function,\n  shallow?: boolean\n) {\n  const dep = new Dep()\n\n  const property = Object.getOwnPropertyDescriptor(obj, key)\n  if (property && property.configurable === false) {\n    return\n  }\n\n  // cater for pre-defined getter/setters\n  const getter = property && property.get\n  const setter = property && property.set\n  if ((!getter || setter) && arguments.length === 2) {\n    val = obj[key]\n  }\n\n  let childOb = !shallow && observe(val)\n  Object.defineProperty(obj, key, {\n    enumerable: true,\n    configurable: true,\n    get: function reactiveGetter () {\n      const value = getter ? getter.call(obj) : val\n      if (Dep.target) {\n        dep.depend() // 收集依赖\n        if (childOb) {\n          childOb.dep.depend()\n          if (Array.isArray(value)) {\n            dependArray(value)\n          }\n        }\n      }\n      return value\n    },\n    set: function reactiveSetter (newVal) {\n      const value = getter ? getter.call(obj) : val\n      /* eslint-disable no-self-compare */\n      if (newVal === value || (newVal !== newVal && value !== value){\n        return\n      }\n      /* eslint-enable no-self-compare */\n      if (process.env.NODE_ENV !== 'production' && customSetter) {\n        customSetter()\n      }\n      // #7981: for accessor properties without setter\n      if (getter && !setter) return\n      if (setter) {\n        setter.call(obj, newVal)\n      } else {\n        val = newVal\n      }\n      childOb = !shallow && observe(newVal)\n      dep.notify() // 当数据发生变化时，事件中心Dep通知订阅者\n    }\n  })\n}\n",[266],{"type":18,"tag":175,"props":267,"children":268},{"__ignoreMap":7},[269],{"type":24,"value":264},{"type":18,"tag":26,"props":271,"children":272},{},[273],{"type":24,"value":274},"Dep的实现",{"type":18,"tag":170,"props":276,"children":278},{"code":277},"//https://github.com/vuejs/vue/blob/v2.6.14/src/core/observer/dep.js\nexport default class Dep {\n  static target: ?Watcher;\n  id: number;\n  subs: Array;\n\n  constructor () {\n    this.id = uid++\n    this.subs = []\n  }\n\n  addSub (sub: Watcher) {\n    this.subs.push(sub)\n  }\n\n  removeSub (sub: Watcher) {\n    remove(this.subs, sub)\n  }\n\n  depend () {\n    if (Dep.target) {\n    Dep.target.addDep(this)\n    }\n  }\n\n  notify () {\n    // stabilize the subscriber list first\n    const subs = this.subs.slice()\n    if (process.env.NODE_ENV !== 'production' && !config.async) {\n      // subs aren't sorted in scheduler if not running async\n      // we need to sort them now to make sure they fire in correct\n      // order\n      subs.sort((a, b) => a.id - b.id)\n    }\n    for (let i = 0, l = subs.length; i \u003C l; i++) {\n      subs[i].update() //当数据发生变化时，通知订阅者更新视图\n    }\n  }\n}\n",[279],{"type":18,"tag":175,"props":280,"children":281},{"__ignoreMap":7},[282],{"type":24,"value":277},{"type":18,"tag":26,"props":284,"children":285},{},[286],{"type":24,"value":287},"Watcher的实现",{"type":18,"tag":170,"props":289,"children":291},{"code":290},"//https://github.com/vuejs/vue/blob/v2.6.14/src/core/observer/watcher.js\n/**\n   * Subscriber interface.\n   * Will be called when a dependency changes.\n   */\n  update () { // 订阅者更新视图方法\n    /* istanbul ignore else */\n    if (this.lazy) {\n      this.dirty = true\n    } else if (this.sync) {\n      this.run()\n    } else {\n      queueWatcher(this)\n    }\n  }\n",[292],{"type":18,"tag":175,"props":293,"children":294},{"__ignoreMap":7},[295],{"type":24,"value":290},{"type":18,"tag":26,"props":297,"children":298},{},[299],{"type":18,"tag":30,"props":300,"children":301},{},[302],{"type":24,"value":303},"04",{"type":18,"tag":26,"props":305,"children":306},{},[307],{"type":18,"tag":30,"props":308,"children":309},{},[310],{"type":24,"value":311},"总结",{"type":18,"tag":26,"props":313,"children":314},{},[315],{"type":24,"value":316},"首先感谢昇思MindSpore社区提供的这次开源机会，这是我第一次参加开源项目，从这次开源经历中让我掌握了项目的开发流程，以及开源社区中的成员如何协同开发维护一个项目，另外此次开源经历，让我了解了小白如何参与开源、如何正确的提交PR。最后，我引用Apache APISIX PMC成员王院生的一句话：”参与开源，让我觉得自己终于与这个世界融为一体，不再是孤立的个体“ ，我也会继续参与开源，持续为开源贡献自己的一份力量。",{"title":7,"searchDepth":318,"depth":318,"links":319},4,[],"markdown","content:technology-blogs:zh:2020.md","content","technology-blogs/zh/2020.md","technology-blogs/zh/2020","md",1776506117726]