前言

本文是vue2.x源码分析的第十篇,主要看computed和watch的处理过程!

实例代码

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Vue</title>
  6. <script src="./vue10.js" type="text/javascript" charset="utf-8" ></script>
  7. </head>
  8. <body>
  9. <div id="app">
  10. {{messages}}
  11. </div>
  12. <script>
  13. var vm=new Vue({
  14. el:'#app',
  15. name:'app',
  16. data:{
  17. message:'message'
  18. },
  19. computed:{
  20. messages(){
  21. return this.message+'s'
  22. }
  23. },
  24. watch:{
  25. message(){
  26. alert('message changed!')
  27. }
  28. }
  29. });
  30. debugger;
  31. setTimeout(
  32. ()=>{
  33. vm.message='messages'
  34. }
  35. ,0)
  36. </script>
  37. </body>
  38. </html>

1 computed

关键断点
initState(vm);
if (opts.computed) { initComputed(vm, opts.computed); }
watchers[key] = new Watcher(vm, getter, noop,
computedWatcherOptions);//option中lazy:true导致watcher.get不执行,从而该watcher不收集dep,dep也不订阅该watcher
defineComputed(vm, key, userDef);
看下defineComputed

  1. function defineComputed (target, key, userDef) {
  2. if (typeof userDef === 'function') {
  3. sharedPropertyDefinition.get = createComputedGetter(key);
  4. sharedPropertyDefinition.set = noop;
  5. } else {
  6. sharedPropertyDefinition.get = userDef.get
  7. ? userDef.cache !== false
  8. ? createComputedGetter(key)
  9. : userDef.get
  10. : noop;
  11. sharedPropertyDefinition.set = userDef.set
  12. ? userDef.set
  13. : noop;
  14. }
  15. Object.defineProperty(target, key, sharedPropertyDefinition);
  16. }

一句话总结computed原理:通过Object.defineProperty将计算属性的key(即messages)定义为vm的存取器属性,该key的get函数即是计算属性的value(即messages对应的函数),key的更新随着它所依赖的data中的某个属性一同更新

2 watch

关键断点
initState(vm);
if (opts.watch) { initWatch(vm, opts.watch); }
createWatcher(vm, key, handler);
vm.$watch(key, handler, options);
看下该函数:

  1. Vue.prototype.$watch = function (expOrFn,cb, options) {
  2. var vm = this;
  3. options = options || {};
  4. options.user = true; //user watcher
  5. var watcher = new Watcher(vm, expOrFn, cb, options);
  6. if (options.immediate) {
  7. cb.call(vm, watcher.value);
  8. }
  9. return function unwatchFn () {
  10. watcher.teardown();
  11. }
  12. };
  1. var Watcher = function Watcher (vm,expOrFn,cb,options) {
  2. ...
  3. this.user=true;
  4. this.cb = cb;
  5. ...
  6. // parse expression for getter
  7. if (typeof expOrFn === 'function') { render watcher从这儿进
  8. this.getter = expOrFn;
  9. } else {
  10. this.getter = parsePath(expOrFn); user watcher从这儿进
  11. }
  12. this.value = this.lazy
  13. ? undefined
  14. : this.get();
  15. };

接下来和render watcher一样执行get函数,然后message的dep订阅该user watcher,随后message的dep又订阅render watcher,也就是说message的dep.sub数组中有两个watcher,分别是user watcher和render watcher,页面初次渲染时user watcher不起什么作用,当message值改变导致页面更新时,先执行user watcher,这会执行该watcher.cb函数也即定义watch时的message的回调函数,从而实现了watch监控message变化的功能,然后执行render watcher实现页面更新。