[{"data":1,"prerenderedAt":1483},["ShallowReactive",2],{"page-\u002Fvue\u002Fvue中computed分析":3},{"id":4,"title":5,"body":6,"description":1476,"extension":1477,"meta":1478,"navigation":693,"path":1479,"seo":1480,"stem":1481,"__hash__":1482},"content\u002FVue\u002FVue中computed分析.md","Vue中computed分析",{"type":7,"value":8,"toc":1470},"minimark",[9,13,33,37,88,719,722,735,784,818,1116,1146,1446,1449,1457,1460,1466],[10,11,5],"h1",{"id":12},"vue中computed分析",[14,15,16,17,21,22,25,26,29,30,32],"p",{},"在",[18,19,20],"code",{},"Vue","中",[18,23,24],{},"computed","是计算属性，其会根据所依赖的数据动态显示新的计算结果，虽然使用",[18,27,28],{},"{{}}","模板内的表达式非常便利，但是设计它们的初衷是用于简单运算的，在模板中放入太多的逻辑会让模板过重且难以维护，所以对于任何复杂逻辑，都应当使用计算属性。计算属性是基于数据的响应式依赖进行缓存的，只在相关响应式依赖发生改变时它们才会重新求值，也就是说只要计算属性依赖的数据还没有发生改变，多次访问计算属性会立即返回之前的计算结果，而不必再次执行函数，当然如果不希望使用缓存可以使用方法属性并返回值即可，",[18,31,24],{},"计算属性非常适用于一个数据受多个数据影响以及需要对数据进行预处理的条件下使用。",[34,35,36],"h2",{"id":36},"描述",[14,38,39,41,42,45,46,48,49,52,53,56,57,60,61,63,64,66,67,70,71,73,74,76,77,80,81,84,85,87],{},[18,40,24],{},"计算属性可以定义两种方式的参数，",[18,43,44],{},"{ [key: string]: Function | { get: Function, set: Function } }","，计算属性直接定义在",[18,47,20],{},"实例中，所有",[18,50,51],{},"getter","和",[18,54,55],{},"setter","的",[18,58,59],{},"this","上下文自动地绑定为",[18,62,20],{},"实例，此外如果为一个计算属性使用了箭头函数，则",[18,65,59],{},"不会指向这个组件的实例，不过仍然可以将其实例作为函数的第一个参数来访问，计算属性的结果会被缓存，除非依赖的响应式",[18,68,69],{},"property","变化才会重新计算，注意如果某个依赖例如非响应式",[18,72,69],{},"在该实例范畴之外，则计算属性是不会被更新的。事实上",[18,75,24],{},"会拥有自己的",[18,78,79],{},"watcher","，其内部有个属性",[18,82,83],{},"dirty","开关来决定",[18,86,24],{},"的值是需要重新计算还是直接复用之前的值。",[89,90,95],"pre",{"className":91,"code":92,"language":93,"meta":94,"style":94},"language-html shiki shiki-themes github-light github-dark","\u003C!DOCTYPE html>\n\u003Chtml>\n\u003Chead>\n    \u003Ctitle>Vue\u003C\u002Ftitle>\n\u003C\u002Fhead>\n\u003Cbody>\n    \u003Cdiv id=\"app\">\u003C\u002Fdiv>\n\u003C\u002Fbody>\n\u003Cscript src=\"https:\u002F\u002Fcdn.bootcss.com\u002Fvue\u002F2.4.2\u002Fvue.js\">\u003C\u002Fscript>\n\u003Cscript type=\"text\u002Fjavascript\">\n    var vm = new Vue({\n        el: \"#app\",\n        data: {\n            a: 1,\n            b: 2\n        },\n        template:`\n            \u003Cdiv>\n                \u003Cdiv>{{multiplication}}\u003C\u002Fdiv>\n                \u003Cdiv>{{multiplication}}\u003C\u002Fdiv>\n                \u003Cdiv>{{multiplication}}\u003C\u002Fdiv>\n                \u003Cdiv>{{multiplicationArrow}}\u003C\u002Fdiv>\n                \u003Cbutton @click=\"updateSetting\">updateSetting\u003C\u002Fbutton>\n            \u003C\u002Fdiv>\n        `,\n        computed:{\n            multiplication: function(){\n                console.log(\"a * b\"); \u002F\u002F 初始只打印一次 返回值被缓存\n                return this.a * this.b;\n            },\n            multiplicationArrow: vm => vm.a * vm.b * 3, \u002F\u002F 箭头函数可以通过传入的参数获取当前实例\n            setting: {\n                get: function(){\n                    console.log(\"a * b * 6\");\n                    return this.a * this.b * 6;\n                },\n                set: function(v){\n                    console.log(`${v} -> a`);\n                    this.a = v;\n                }\n            }\n        },\n        methods:{\n            updateSetting: function(){ \u002F\u002F 点击按钮后\n                console.log(this.setting); \u002F\u002F 12\n                this.setting = 3; \u002F\u002F 3 -> a\n                console.log(this.setting); \u002F\u002F 36\n            }\n        },\n\n    })\n\u003C\u002Fscript>\n\u003C\u002Fhtml>\n","html","",[18,96,97,117,127,137,153,163,173,198,207,229,246,267,279,285,297,306,312,321,327,333,338,343,349,355,361,369,375,390,412,432,438,472,478,490,506,531,537,555,574,587,593,599,604,610,626,643,662,678,683,688,695,701,710],{"__ignoreMap":94},[98,99,102,106,110,114],"span",{"class":100,"line":101},"line",1,[98,103,105],{"class":104},"sVt8B","\u003C!",[98,107,109],{"class":108},"s9eBZ","DOCTYPE",[98,111,113],{"class":112},"sScJk"," html",[98,115,116],{"class":104},">\n",[98,118,120,123,125],{"class":100,"line":119},2,[98,121,122],{"class":104},"\u003C",[98,124,93],{"class":108},[98,126,116],{"class":104},[98,128,130,132,135],{"class":100,"line":129},3,[98,131,122],{"class":104},[98,133,134],{"class":108},"head",[98,136,116],{"class":104},[98,138,140,143,146,149,151],{"class":100,"line":139},4,[98,141,142],{"class":104},"    \u003C",[98,144,145],{"class":108},"title",[98,147,148],{"class":104},">Vue\u003C\u002F",[98,150,145],{"class":108},[98,152,116],{"class":104},[98,154,156,159,161],{"class":100,"line":155},5,[98,157,158],{"class":104},"\u003C\u002F",[98,160,134],{"class":108},[98,162,116],{"class":104},[98,164,166,168,171],{"class":100,"line":165},6,[98,167,122],{"class":104},[98,169,170],{"class":108},"body",[98,172,116],{"class":104},[98,174,176,178,181,184,187,191,194,196],{"class":100,"line":175},7,[98,177,142],{"class":104},[98,179,180],{"class":108},"div",[98,182,183],{"class":112}," id",[98,185,186],{"class":104},"=",[98,188,190],{"class":189},"sZZnC","\"app\"",[98,192,193],{"class":104},">\u003C\u002F",[98,195,180],{"class":108},[98,197,116],{"class":104},[98,199,201,203,205],{"class":100,"line":200},8,[98,202,158],{"class":104},[98,204,170],{"class":108},[98,206,116],{"class":104},[98,208,210,212,215,218,220,223,225,227],{"class":100,"line":209},9,[98,211,122],{"class":104},[98,213,214],{"class":108},"script",[98,216,217],{"class":112}," src",[98,219,186],{"class":104},[98,221,222],{"class":189},"\"https:\u002F\u002Fcdn.bootcss.com\u002Fvue\u002F2.4.2\u002Fvue.js\"",[98,224,193],{"class":104},[98,226,214],{"class":108},[98,228,116],{"class":104},[98,230,232,234,236,239,241,244],{"class":100,"line":231},10,[98,233,122],{"class":104},[98,235,214],{"class":108},[98,237,238],{"class":112}," type",[98,240,186],{"class":104},[98,242,243],{"class":189},"\"text\u002Fjavascript\"",[98,245,116],{"class":104},[98,247,249,253,256,258,261,264],{"class":100,"line":248},11,[98,250,252],{"class":251},"szBVR","    var",[98,254,255],{"class":104}," vm ",[98,257,186],{"class":251},[98,259,260],{"class":251}," new",[98,262,263],{"class":112}," Vue",[98,265,266],{"class":104},"({\n",[98,268,270,273,276],{"class":100,"line":269},12,[98,271,272],{"class":104},"        el: ",[98,274,275],{"class":189},"\"#app\"",[98,277,278],{"class":104},",\n",[98,280,282],{"class":100,"line":281},13,[98,283,284],{"class":104},"        data: {\n",[98,286,288,291,295],{"class":100,"line":287},14,[98,289,290],{"class":104},"            a: ",[98,292,294],{"class":293},"sj4cs","1",[98,296,278],{"class":104},[98,298,300,303],{"class":100,"line":299},15,[98,301,302],{"class":104},"            b: ",[98,304,305],{"class":293},"2\n",[98,307,309],{"class":100,"line":308},16,[98,310,311],{"class":104},"        },\n",[98,313,315,318],{"class":100,"line":314},17,[98,316,317],{"class":104},"        template:",[98,319,320],{"class":189},"`\n",[98,322,324],{"class":100,"line":323},18,[98,325,326],{"class":189},"            \u003Cdiv>\n",[98,328,330],{"class":100,"line":329},19,[98,331,332],{"class":189},"                \u003Cdiv>{{multiplication}}\u003C\u002Fdiv>\n",[98,334,336],{"class":100,"line":335},20,[98,337,332],{"class":189},[98,339,341],{"class":100,"line":340},21,[98,342,332],{"class":189},[98,344,346],{"class":100,"line":345},22,[98,347,348],{"class":189},"                \u003Cdiv>{{multiplicationArrow}}\u003C\u002Fdiv>\n",[98,350,352],{"class":100,"line":351},23,[98,353,354],{"class":189},"                \u003Cbutton @click=\"updateSetting\">updateSetting\u003C\u002Fbutton>\n",[98,356,358],{"class":100,"line":357},24,[98,359,360],{"class":189},"            \u003C\u002Fdiv>\n",[98,362,364,367],{"class":100,"line":363},25,[98,365,366],{"class":189},"        `",[98,368,278],{"class":104},[98,370,372],{"class":100,"line":371},26,[98,373,374],{"class":104},"        computed:{\n",[98,376,378,381,384,387],{"class":100,"line":377},27,[98,379,380],{"class":112},"            multiplication",[98,382,383],{"class":104},": ",[98,385,386],{"class":251},"function",[98,388,389],{"class":104},"(){\n",[98,391,393,396,399,402,405,408],{"class":100,"line":392},28,[98,394,395],{"class":104},"                console.",[98,397,398],{"class":112},"log",[98,400,401],{"class":104},"(",[98,403,404],{"class":189},"\"a * b\"",[98,406,407],{"class":104},"); ",[98,409,411],{"class":410},"sJ8bj","\u002F\u002F 初始只打印一次 返回值被缓存\n",[98,413,415,418,421,424,427,429],{"class":100,"line":414},29,[98,416,417],{"class":251},"                return",[98,419,420],{"class":293}," this",[98,422,423],{"class":104},".a ",[98,425,426],{"class":251},"*",[98,428,420],{"class":293},[98,430,431],{"class":104},".b;\n",[98,433,435],{"class":100,"line":434},30,[98,436,437],{"class":104},"            },\n",[98,439,441,444,446,450,453,456,458,461,463,466,469],{"class":100,"line":440},31,[98,442,443],{"class":112},"            multiplicationArrow",[98,445,383],{"class":104},[98,447,449],{"class":448},"s4XuR","vm",[98,451,452],{"class":251}," =>",[98,454,455],{"class":104}," vm.a ",[98,457,426],{"class":251},[98,459,460],{"class":104}," vm.b ",[98,462,426],{"class":251},[98,464,465],{"class":293}," 3",[98,467,468],{"class":104},", ",[98,470,471],{"class":410},"\u002F\u002F 箭头函数可以通过传入的参数获取当前实例\n",[98,473,475],{"class":100,"line":474},32,[98,476,477],{"class":104},"            setting: {\n",[98,479,481,484,486,488],{"class":100,"line":480},33,[98,482,483],{"class":112},"                get",[98,485,383],{"class":104},[98,487,386],{"class":251},[98,489,389],{"class":104},[98,491,493,496,498,500,503],{"class":100,"line":492},34,[98,494,495],{"class":104},"                    console.",[98,497,398],{"class":112},[98,499,401],{"class":104},[98,501,502],{"class":189},"\"a * b * 6\"",[98,504,505],{"class":104},");\n",[98,507,509,512,514,516,518,520,523,525,528],{"class":100,"line":508},35,[98,510,511],{"class":251},"                    return",[98,513,420],{"class":293},[98,515,423],{"class":104},[98,517,426],{"class":251},[98,519,420],{"class":293},[98,521,522],{"class":104},".b ",[98,524,426],{"class":251},[98,526,527],{"class":293}," 6",[98,529,530],{"class":104},";\n",[98,532,534],{"class":100,"line":533},36,[98,535,536],{"class":104},"                },\n",[98,538,540,543,545,547,549,552],{"class":100,"line":539},37,[98,541,542],{"class":112},"                set",[98,544,383],{"class":104},[98,546,386],{"class":251},[98,548,401],{"class":104},[98,550,551],{"class":448},"v",[98,553,554],{"class":104},"){\n",[98,556,558,560,562,564,567,569,572],{"class":100,"line":557},38,[98,559,495],{"class":104},[98,561,398],{"class":112},[98,563,401],{"class":104},[98,565,566],{"class":189},"`${",[98,568,551],{"class":104},[98,570,571],{"class":189},"} -> a`",[98,573,505],{"class":104},[98,575,577,580,582,584],{"class":100,"line":576},39,[98,578,579],{"class":293},"                    this",[98,581,423],{"class":104},[98,583,186],{"class":251},[98,585,586],{"class":104}," v;\n",[98,588,590],{"class":100,"line":589},40,[98,591,592],{"class":104},"                }\n",[98,594,596],{"class":100,"line":595},41,[98,597,598],{"class":104},"            }\n",[98,600,602],{"class":100,"line":601},42,[98,603,311],{"class":104},[98,605,607],{"class":100,"line":606},43,[98,608,609],{"class":104},"        methods:{\n",[98,611,613,616,618,620,623],{"class":100,"line":612},44,[98,614,615],{"class":112},"            updateSetting",[98,617,383],{"class":104},[98,619,386],{"class":251},[98,621,622],{"class":104},"(){ ",[98,624,625],{"class":410},"\u002F\u002F 点击按钮后\n",[98,627,629,631,633,635,637,640],{"class":100,"line":628},45,[98,630,395],{"class":104},[98,632,398],{"class":112},[98,634,401],{"class":104},[98,636,59],{"class":293},[98,638,639],{"class":104},".setting); ",[98,641,642],{"class":410},"\u002F\u002F 12\n",[98,644,646,649,652,654,656,659],{"class":100,"line":645},46,[98,647,648],{"class":293},"                this",[98,650,651],{"class":104},".setting ",[98,653,186],{"class":251},[98,655,465],{"class":293},[98,657,658],{"class":104},"; ",[98,660,661],{"class":410},"\u002F\u002F 3 -> a\n",[98,663,665,667,669,671,673,675],{"class":100,"line":664},47,[98,666,395],{"class":104},[98,668,398],{"class":112},[98,670,401],{"class":104},[98,672,59],{"class":293},[98,674,639],{"class":104},[98,676,677],{"class":410},"\u002F\u002F 36\n",[98,679,681],{"class":100,"line":680},48,[98,682,598],{"class":104},[98,684,686],{"class":100,"line":685},49,[98,687,311],{"class":104},[98,689,691],{"class":100,"line":690},50,[98,692,694],{"emptyLinePlaceholder":693},true,"\n",[98,696,698],{"class":100,"line":697},51,[98,699,700],{"class":104},"    })\n",[98,702,704,706,708],{"class":100,"line":703},52,[98,705,158],{"class":104},[98,707,214],{"class":108},[98,709,116],{"class":104},[98,711,713,715,717],{"class":100,"line":712},53,[98,714,158],{"class":104},[98,716,93],{"class":108},[98,718,116],{"class":104},[34,720,721],{"id":721},"分析",[14,723,724,725,727,728,731,732,734],{},"首先在",[18,726,20],{},"中完成双向绑定是通过",[18,729,730],{},"Object.defineProperty()","实现的，",[18,733,20],{},"的双向数据绑定，简单点来说分为以下三个部分：",[736,737,738,754,762],"ul",{},[739,740,741,744,745,749,750,753],"li",{},[18,742,743],{},"Observer",": 这里的主要工作是",[746,747,748],"strong",{},"递归地监听对象上的所有属性","，在属性值改变的时候，触发相应的",[18,751,752],{},"Watcher","。",[739,755,756,758,759,761],{},[18,757,752],{},": 观察者，当监听的数据值修改时，执行响应的回调函数，在",[18,760,20],{},"里面的更新模板内容。",[739,763,764,767,768,52,770,772,773,775,776,778,779,781,782,753],{},[18,765,766],{},"Dep",": 链接",[18,769,743],{},[18,771,752],{},"的桥梁，每一个",[18,774,743],{},"对应一个",[18,777,766],{},"，它内部维护一个数组，保存与该",[18,780,743],{},"相关的",[18,783,752],{},[14,785,786,788,789,792,793,753,796,799,800,803,804,806,807,810,811,814,815,817],{},[18,787,20],{},"源码的实现比较复杂，会处理各种兼容问题与异常以及各种条件分支，文章分析比较核心的代码部分，精简过后的版本，重要部分做出注释，",[18,790,791],{},"commit id","为",[18,794,795],{},"0664cb0",[797,798],"br",{},"\n首先在",[18,801,802],{},"dev\u002Fsrc\u002Fcore\u002Finstance\u002Fstate.js","中定义了初始化",[18,805,24],{},"以及",[18,808,809],{},"initComputed","函数的实现，现在暂不考虑",[18,812,813],{},"SSR","服务端渲染的",[18,816,24],{},"实现。",[89,819,823],{"className":820,"code":821,"language":822,"meta":94,"style":94},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F dev\u002Fsrc\u002Fcore\u002Finstance\u002Fstate.js line 47\nexport function initState (vm: Component) {\n  vm._watchers = []\n  const opts = vm.$options \u002F\u002F 获取组件定义的选项\n  if (opts.props) initProps(vm, opts.props)\n  if (opts.methods) initMethods(vm, opts.methods)\n  if (opts.data) {\n    initData(vm)\n  } else {\n    observe(vm._data = {}, true \u002F* asRootData *\u002F)\n  }\n  if (opts.computed) initComputed(vm, opts.computed) \u002F\u002F 定义computed属性则进行初始化\n  if (opts.watch && opts.watch !== nativeWatch) {\n    initWatch(vm, opts.watch)\n  }\n}\n\n\u002F\u002F dev\u002Fsrc\u002Fcore\u002Finstance\u002Fstate.js line 169\nfunction initComputed (vm: Component, computed: Object) {\n  \u002F\u002F $flow-disable-line\n  const watchers = vm._computedWatchers = Object.create(null) \u002F\u002F 创建一个没有原型链指向的对象\n  \u002F\u002F computed properties are just getters during SSR\n  const isSSR = isServerRendering()\n\n  for (const key in computed) {\n    const userDef = computed[key] \u002F\u002F 获取计算属性的key值定义\n    const getter = typeof userDef === 'function' ? userDef : userDef.get \u002F\u002F 由于计算属性接受两种类型的参数 此处判断用以获取getter\n    if (process.env.NODE_ENV !== 'production' && getter == null) {\n      warn(\n        `Getter is missing for computed property \"${key}\".`,\n        vm\n      )\n    }\n\n    if (!isSSR) {\n      \u002F\u002F create internal watcher for the computed property.\n      \u002F\u002F 生成computed watcher(vm, getter, noop, { lazy: true })\n      watchers[key] = new Watcher( \u002F\u002F 计算属性创建观察者watcher和消息订阅器dep\n        vm,\n        getter || noop,\n        noop,\n        computedWatcherOptions\n      )\n    }\n\n    \u002F\u002F component-defined computed properties are already defined on the\n    \u002F\u002F component prototype. We only need to define computed properties defined\n    \u002F\u002F at instantiation here.\n    if (!(key in vm)) { \u002F\u002F 检查重名属性\n      defineComputed(vm, key, userDef) \u002F\u002F 定义属性\n    } else if (process.env.NODE_ENV !== 'production') {\n      if (key in vm.$data) {\n        warn(`The computed property \"${key}\" is already defined in data.`, vm)\n      } else if (vm.$options.props && key in vm.$options.props) {\n        warn(`The computed property \"${key}\" is already defined as a prop.`, vm)\n      }\n    }\n  }\n}\n","javascript",[18,824,825,830,835,840,845,850,855,860,865,870,875,880,885,890,895,899,904,908,913,918,923,928,933,938,942,947,952,957,962,967,972,977,982,987,991,996,1001,1006,1011,1016,1021,1026,1031,1035,1039,1043,1048,1053,1058,1063,1068,1073,1078,1083,1089,1095,1101,1106,1111],{"__ignoreMap":94},[98,826,827],{"class":100,"line":101},[98,828,829],{},"\u002F\u002F dev\u002Fsrc\u002Fcore\u002Finstance\u002Fstate.js line 47\n",[98,831,832],{"class":100,"line":119},[98,833,834],{},"export function initState (vm: Component) {\n",[98,836,837],{"class":100,"line":129},[98,838,839],{},"  vm._watchers = []\n",[98,841,842],{"class":100,"line":139},[98,843,844],{},"  const opts = vm.$options \u002F\u002F 获取组件定义的选项\n",[98,846,847],{"class":100,"line":155},[98,848,849],{},"  if (opts.props) initProps(vm, opts.props)\n",[98,851,852],{"class":100,"line":165},[98,853,854],{},"  if (opts.methods) initMethods(vm, opts.methods)\n",[98,856,857],{"class":100,"line":175},[98,858,859],{},"  if (opts.data) {\n",[98,861,862],{"class":100,"line":200},[98,863,864],{},"    initData(vm)\n",[98,866,867],{"class":100,"line":209},[98,868,869],{},"  } else {\n",[98,871,872],{"class":100,"line":231},[98,873,874],{},"    observe(vm._data = {}, true \u002F* asRootData *\u002F)\n",[98,876,877],{"class":100,"line":248},[98,878,879],{},"  }\n",[98,881,882],{"class":100,"line":269},[98,883,884],{},"  if (opts.computed) initComputed(vm, opts.computed) \u002F\u002F 定义computed属性则进行初始化\n",[98,886,887],{"class":100,"line":281},[98,888,889],{},"  if (opts.watch && opts.watch !== nativeWatch) {\n",[98,891,892],{"class":100,"line":287},[98,893,894],{},"    initWatch(vm, opts.watch)\n",[98,896,897],{"class":100,"line":299},[98,898,879],{},[98,900,901],{"class":100,"line":308},[98,902,903],{},"}\n",[98,905,906],{"class":100,"line":314},[98,907,694],{"emptyLinePlaceholder":693},[98,909,910],{"class":100,"line":323},[98,911,912],{},"\u002F\u002F dev\u002Fsrc\u002Fcore\u002Finstance\u002Fstate.js line 169\n",[98,914,915],{"class":100,"line":329},[98,916,917],{},"function initComputed (vm: Component, computed: Object) {\n",[98,919,920],{"class":100,"line":335},[98,921,922],{},"  \u002F\u002F $flow-disable-line\n",[98,924,925],{"class":100,"line":340},[98,926,927],{},"  const watchers = vm._computedWatchers = Object.create(null) \u002F\u002F 创建一个没有原型链指向的对象\n",[98,929,930],{"class":100,"line":345},[98,931,932],{},"  \u002F\u002F computed properties are just getters during SSR\n",[98,934,935],{"class":100,"line":351},[98,936,937],{},"  const isSSR = isServerRendering()\n",[98,939,940],{"class":100,"line":357},[98,941,694],{"emptyLinePlaceholder":693},[98,943,944],{"class":100,"line":363},[98,945,946],{},"  for (const key in computed) {\n",[98,948,949],{"class":100,"line":371},[98,950,951],{},"    const userDef = computed[key] \u002F\u002F 获取计算属性的key值定义\n",[98,953,954],{"class":100,"line":377},[98,955,956],{},"    const getter = typeof userDef === 'function' ? userDef : userDef.get \u002F\u002F 由于计算属性接受两种类型的参数 此处判断用以获取getter\n",[98,958,959],{"class":100,"line":392},[98,960,961],{},"    if (process.env.NODE_ENV !== 'production' && getter == null) {\n",[98,963,964],{"class":100,"line":414},[98,965,966],{},"      warn(\n",[98,968,969],{"class":100,"line":434},[98,970,971],{},"        `Getter is missing for computed property \"${key}\".`,\n",[98,973,974],{"class":100,"line":440},[98,975,976],{},"        vm\n",[98,978,979],{"class":100,"line":474},[98,980,981],{},"      )\n",[98,983,984],{"class":100,"line":480},[98,985,986],{},"    }\n",[98,988,989],{"class":100,"line":492},[98,990,694],{"emptyLinePlaceholder":693},[98,992,993],{"class":100,"line":508},[98,994,995],{},"    if (!isSSR) {\n",[98,997,998],{"class":100,"line":533},[98,999,1000],{},"      \u002F\u002F create internal watcher for the computed property.\n",[98,1002,1003],{"class":100,"line":539},[98,1004,1005],{},"      \u002F\u002F 生成computed watcher(vm, getter, noop, { lazy: true })\n",[98,1007,1008],{"class":100,"line":557},[98,1009,1010],{},"      watchers[key] = new Watcher( \u002F\u002F 计算属性创建观察者watcher和消息订阅器dep\n",[98,1012,1013],{"class":100,"line":576},[98,1014,1015],{},"        vm,\n",[98,1017,1018],{"class":100,"line":589},[98,1019,1020],{},"        getter || noop,\n",[98,1022,1023],{"class":100,"line":595},[98,1024,1025],{},"        noop,\n",[98,1027,1028],{"class":100,"line":601},[98,1029,1030],{},"        computedWatcherOptions\n",[98,1032,1033],{"class":100,"line":606},[98,1034,981],{},[98,1036,1037],{"class":100,"line":612},[98,1038,986],{},[98,1040,1041],{"class":100,"line":628},[98,1042,694],{"emptyLinePlaceholder":693},[98,1044,1045],{"class":100,"line":645},[98,1046,1047],{},"    \u002F\u002F component-defined computed properties are already defined on the\n",[98,1049,1050],{"class":100,"line":664},[98,1051,1052],{},"    \u002F\u002F component prototype. We only need to define computed properties defined\n",[98,1054,1055],{"class":100,"line":680},[98,1056,1057],{},"    \u002F\u002F at instantiation here.\n",[98,1059,1060],{"class":100,"line":685},[98,1061,1062],{},"    if (!(key in vm)) { \u002F\u002F 检查重名属性\n",[98,1064,1065],{"class":100,"line":690},[98,1066,1067],{},"      defineComputed(vm, key, userDef) \u002F\u002F 定义属性\n",[98,1069,1070],{"class":100,"line":697},[98,1071,1072],{},"    } else if (process.env.NODE_ENV !== 'production') {\n",[98,1074,1075],{"class":100,"line":703},[98,1076,1077],{},"      if (key in vm.$data) {\n",[98,1079,1080],{"class":100,"line":712},[98,1081,1082],{},"        warn(`The computed property \"${key}\" is already defined in data.`, vm)\n",[98,1084,1086],{"class":100,"line":1085},54,[98,1087,1088],{},"      } else if (vm.$options.props && key in vm.$options.props) {\n",[98,1090,1092],{"class":100,"line":1091},55,[98,1093,1094],{},"        warn(`The computed property \"${key}\" is already defined as a prop.`, vm)\n",[98,1096,1098],{"class":100,"line":1097},56,[98,1099,1100],{},"      }\n",[98,1102,1104],{"class":100,"line":1103},57,[98,1105,986],{},[98,1107,1109],{"class":100,"line":1108},58,[98,1110,879],{},[98,1112,1114],{"class":100,"line":1113},59,[98,1115,903],{},[14,1117,1118,1121,1122,1124,1125,806,1128,1131,1132,1135,1136,52,1138,1141,1142,1145],{},[18,1119,1120],{},"defineComputed","传入了三个参数，",[18,1123,449],{},"实例、计算属性的",[18,1126,1127],{},"key",[18,1129,1130],{},"userDef","计算属性的定义，属性描述符",[18,1133,1134],{},"sharedPropertyDefinition","在初始化定义之后经过",[18,1137,1130],{},[18,1139,1140],{},"shouldCache","等多重判断后被重写，进而通过",[18,1143,1144],{},"Object.defineProperty(target, key, sharedPropertyDefinition)","进行属性的定义。",[89,1147,1149],{"className":820,"code":1148,"language":822,"meta":94,"style":94},"\u002F\u002F dev\u002Fsrc\u002Fcore\u002Finstance\u002Fstate.js line 31\nconst sharedPropertyDefinition = {\n  enumerable: true,\n  configurable: true,\n  get: noop,\n  set: noop\n}\n\n\u002F\u002F dev\u002Fsrc\u002Fcore\u002Finstance\u002Fstate.js line 210\nexport function defineComputed (\n  target: any,\n  key: string,\n  userDef: Object | Function\n) {\n  const shouldCache = !isServerRendering()\n  if (typeof userDef === 'function') {\n    sharedPropertyDefinition.get = shouldCache\n      ? createComputedGetter(key)\n      : createGetterInvoker(userDef)\n    sharedPropertyDefinition.set = noop\n  } else {\n    sharedPropertyDefinition.get = userDef.get\n      ? shouldCache && userDef.cache !== false\n        ? createComputedGetter(key)\n        : createGetterInvoker(userDef.get)\n      : noop\n    sharedPropertyDefinition.set = userDef.set || noop\n  }\n  if (process.env.NODE_ENV !== 'production' &&\n      sharedPropertyDefinition.set === noop) {\n    sharedPropertyDefinition.set = function () {\n      warn(\n        `Computed property \"${key}\" was assigned to but it has no setter.`,\n        this\n      )\n    }\n  }\n  Object.defineProperty(target, key, sharedPropertyDefinition)\n}\n\n\u002F**\n 经过重写之后的属性描述符在某条件分支大致呈现如下\n sharedPropertyDefinition = {\n    enumerable: true,\n    configurable: true,\n    get: function computedGetter () {\n      const watcher = this._computedWatchers && this._computedWatchers[key]\n      if (watcher) {\n        if (watcher.dirty) {\n          watcher.evaluate()\n        }\n        if (Dep.target) {\n          watcher.depend()\n        }\n        return watcher.value\n      }\n    },\n    set: userDef.set || noop\n } \n 当计算属性被调用时便会执行 get 访问函数，从而关联上观察者对象 watcher 然后执行 wather.depend() 收集依赖和 watcher.evaluate() 计算求值。\n*\u002F\n",[18,1150,1151,1156,1161,1166,1171,1176,1181,1185,1189,1194,1199,1204,1209,1214,1219,1224,1229,1234,1239,1244,1249,1253,1258,1263,1268,1273,1278,1283,1287,1292,1297,1302,1306,1311,1316,1320,1324,1328,1333,1337,1341,1346,1351,1356,1361,1366,1371,1376,1381,1386,1391,1396,1401,1406,1410,1415,1419,1424,1429,1434,1440],{"__ignoreMap":94},[98,1152,1153],{"class":100,"line":101},[98,1154,1155],{},"\u002F\u002F dev\u002Fsrc\u002Fcore\u002Finstance\u002Fstate.js line 31\n",[98,1157,1158],{"class":100,"line":119},[98,1159,1160],{},"const sharedPropertyDefinition = {\n",[98,1162,1163],{"class":100,"line":129},[98,1164,1165],{},"  enumerable: true,\n",[98,1167,1168],{"class":100,"line":139},[98,1169,1170],{},"  configurable: true,\n",[98,1172,1173],{"class":100,"line":155},[98,1174,1175],{},"  get: noop,\n",[98,1177,1178],{"class":100,"line":165},[98,1179,1180],{},"  set: noop\n",[98,1182,1183],{"class":100,"line":175},[98,1184,903],{},[98,1186,1187],{"class":100,"line":200},[98,1188,694],{"emptyLinePlaceholder":693},[98,1190,1191],{"class":100,"line":209},[98,1192,1193],{},"\u002F\u002F dev\u002Fsrc\u002Fcore\u002Finstance\u002Fstate.js line 210\n",[98,1195,1196],{"class":100,"line":231},[98,1197,1198],{},"export function defineComputed (\n",[98,1200,1201],{"class":100,"line":248},[98,1202,1203],{},"  target: any,\n",[98,1205,1206],{"class":100,"line":269},[98,1207,1208],{},"  key: string,\n",[98,1210,1211],{"class":100,"line":281},[98,1212,1213],{},"  userDef: Object | Function\n",[98,1215,1216],{"class":100,"line":287},[98,1217,1218],{},") {\n",[98,1220,1221],{"class":100,"line":299},[98,1222,1223],{},"  const shouldCache = !isServerRendering()\n",[98,1225,1226],{"class":100,"line":308},[98,1227,1228],{},"  if (typeof userDef === 'function') {\n",[98,1230,1231],{"class":100,"line":314},[98,1232,1233],{},"    sharedPropertyDefinition.get = shouldCache\n",[98,1235,1236],{"class":100,"line":323},[98,1237,1238],{},"      ? createComputedGetter(key)\n",[98,1240,1241],{"class":100,"line":329},[98,1242,1243],{},"      : createGetterInvoker(userDef)\n",[98,1245,1246],{"class":100,"line":335},[98,1247,1248],{},"    sharedPropertyDefinition.set = noop\n",[98,1250,1251],{"class":100,"line":340},[98,1252,869],{},[98,1254,1255],{"class":100,"line":345},[98,1256,1257],{},"    sharedPropertyDefinition.get = userDef.get\n",[98,1259,1260],{"class":100,"line":351},[98,1261,1262],{},"      ? shouldCache && userDef.cache !== false\n",[98,1264,1265],{"class":100,"line":357},[98,1266,1267],{},"        ? createComputedGetter(key)\n",[98,1269,1270],{"class":100,"line":363},[98,1271,1272],{},"        : createGetterInvoker(userDef.get)\n",[98,1274,1275],{"class":100,"line":371},[98,1276,1277],{},"      : noop\n",[98,1279,1280],{"class":100,"line":377},[98,1281,1282],{},"    sharedPropertyDefinition.set = userDef.set || noop\n",[98,1284,1285],{"class":100,"line":392},[98,1286,879],{},[98,1288,1289],{"class":100,"line":414},[98,1290,1291],{},"  if (process.env.NODE_ENV !== 'production' &&\n",[98,1293,1294],{"class":100,"line":434},[98,1295,1296],{},"      sharedPropertyDefinition.set === noop) {\n",[98,1298,1299],{"class":100,"line":440},[98,1300,1301],{},"    sharedPropertyDefinition.set = function () {\n",[98,1303,1304],{"class":100,"line":474},[98,1305,966],{},[98,1307,1308],{"class":100,"line":480},[98,1309,1310],{},"        `Computed property \"${key}\" was assigned to but it has no setter.`,\n",[98,1312,1313],{"class":100,"line":492},[98,1314,1315],{},"        this\n",[98,1317,1318],{"class":100,"line":508},[98,1319,981],{},[98,1321,1322],{"class":100,"line":533},[98,1323,986],{},[98,1325,1326],{"class":100,"line":539},[98,1327,879],{},[98,1329,1330],{"class":100,"line":557},[98,1331,1332],{},"  Object.defineProperty(target, key, sharedPropertyDefinition)\n",[98,1334,1335],{"class":100,"line":576},[98,1336,903],{},[98,1338,1339],{"class":100,"line":589},[98,1340,694],{"emptyLinePlaceholder":693},[98,1342,1343],{"class":100,"line":595},[98,1344,1345],{},"\u002F**\n",[98,1347,1348],{"class":100,"line":601},[98,1349,1350],{}," 经过重写之后的属性描述符在某条件分支大致呈现如下\n",[98,1352,1353],{"class":100,"line":606},[98,1354,1355],{}," sharedPropertyDefinition = {\n",[98,1357,1358],{"class":100,"line":612},[98,1359,1360],{},"    enumerable: true,\n",[98,1362,1363],{"class":100,"line":628},[98,1364,1365],{},"    configurable: true,\n",[98,1367,1368],{"class":100,"line":645},[98,1369,1370],{},"    get: function computedGetter () {\n",[98,1372,1373],{"class":100,"line":664},[98,1374,1375],{},"      const watcher = this._computedWatchers && this._computedWatchers[key]\n",[98,1377,1378],{"class":100,"line":680},[98,1379,1380],{},"      if (watcher) {\n",[98,1382,1383],{"class":100,"line":685},[98,1384,1385],{},"        if (watcher.dirty) {\n",[98,1387,1388],{"class":100,"line":690},[98,1389,1390],{},"          watcher.evaluate()\n",[98,1392,1393],{"class":100,"line":697},[98,1394,1395],{},"        }\n",[98,1397,1398],{"class":100,"line":703},[98,1399,1400],{},"        if (Dep.target) {\n",[98,1402,1403],{"class":100,"line":712},[98,1404,1405],{},"          watcher.depend()\n",[98,1407,1408],{"class":100,"line":1085},[98,1409,1395],{},[98,1411,1412],{"class":100,"line":1091},[98,1413,1414],{},"        return watcher.value\n",[98,1416,1417],{"class":100,"line":1097},[98,1418,1100],{},[98,1420,1421],{"class":100,"line":1103},[98,1422,1423],{},"    },\n",[98,1425,1426],{"class":100,"line":1108},[98,1427,1428],{},"    set: userDef.set || noop\n",[98,1430,1431],{"class":100,"line":1113},[98,1432,1433],{}," } \n",[98,1435,1437],{"class":100,"line":1436},60,[98,1438,1439],{}," 当计算属性被调用时便会执行 get 访问函数，从而关联上观察者对象 watcher 然后执行 wather.depend() 收集依赖和 watcher.evaluate() 计算求值。\n",[98,1441,1443],{"class":100,"line":1442},61,[98,1444,1445],{},"*\u002F\n",[34,1447,1448],{"id":1448},"每日一题",[89,1450,1455],{"className":1451,"code":1453,"language":1454},[1452],"language-text","https:\u002F\u002Fgithub.com\u002FWindrunnerMax\u002FEveryDay\n","text",[18,1456,1453],{"__ignoreMap":94},[34,1458,1459],{"id":1459},"参考",[89,1461,1464],{"className":1462,"code":1463,"language":1454},[1452],"https:\u002F\u002Fcn.vuejs.org\u002Fv2\u002Fapi\u002F#computed\nhttps:\u002F\u002Fjuejin.im\u002Fpost\u002F6844903678533451783\nhttps:\u002F\u002Fjuejin.im\u002Fpost\u002F6844903873925087239\nhttps:\u002F\u002Fcn.vuejs.org\u002Fv2\u002Fguide\u002Fcomputed.html\nhttps:\u002F\u002Fzheyaoa.github.io\u002F2019\u002F09\u002F07\u002Fcomputed\u002F\nhttps:\u002F\u002Fwww.cnblogs.com\u002Ftugenhua0707\u002Fp\u002F11760466.html\n",[18,1465,1463],{"__ignoreMap":94},[1467,1468,1469],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":94,"searchDepth":119,"depth":119,"links":1471},[1472,1473,1474,1475],{"id":36,"depth":119,"text":36},{"id":721,"depth":119,"text":721},{"id":1448,"depth":119,"text":1448},{"id":1459,"depth":119,"text":1459},"在Vue中computed是计算属性，其会根据所依赖的数据动态显示新的计算结果，虽然使用{{}}模板内的表达式非常便利，但是设计它们的初衷是用于简单运算的，在模板中放入太多的逻辑会让模板过重且难以维护，所以对于任何复杂逻辑，都应当使用计算属性。计算属性是基于数据的响应式依赖进行缓存的，只在相关响应式依赖发生改变时它们才会重新求值，也就是说只要计算属性依赖的数据还没有发生改变，多次访问计算属性会立即返回之前的计算结果，而不必再次执行函数，当然如果不希望使用缓存可以使用方法属性并返回值即可，computed计算属性非常适用于一个数据受多个数据影响以及需要对数据进行预处理的条件下使用。","md",{},"\u002Fvue\u002Fvue中computed分析",{"title":5,"description":1476},"Vue\u002FVue中computed分析","I1fn5YdTtDMX0NDU7mezKZKcSRU3JcVlRxNOFZdOVYc",1791042469237]