Coder Social home page Coder Social logo

learnvue's Introduction

learnVue

介绍

Vue.js源码分析,记录了个人学习Vue.js源码的过程中的一些心得以及收获。以及对于Vue框架,周边库的一些个人见解。

在学习的过程中我为Vue.js(2.3.0)、Vuex(2.4.0)、Vue-router(3.0.1)加上了注释,分别在文件夹vue-srcvuex-src以及vue-router-src中,希望可以帮助有需要的同学更好地学习理解Vue.js及周边库的源码。

感谢尤大提高生产力。

本项目希望对Vue.js做更进一步的探索与学习,Vue.js基础内容请参考Vue.js官网,https://cn.vuejs.org/v2/guide/。 可能会有理解存在偏差的地方,欢迎提issue指出,共同学习,共同进步。


目录

源码相关

Vue.js响应式原理

Vue.js依赖收集

从Vue.js源码角度再看数据绑定

Vue.js事件机制

VNode节点(Vue.js实现)

Virtual DOM与diff(Vue.js实现)

聊聊Vue.js的template编译

Vue.js异步更新DOM策略及nextTick

从template到DOM(Vue.js源码角度看内部运行机制)

Vuex源码解析

聊聊keep-alive组件的使用及其实现原理

随笔杂谈

Vue组件间通信

说说element组件库broadcast与dispatch


对于新手同学

由于以上内容都是针对 Vue.js 源码进行讲解了,可能有一些不太熟悉源码的同学读起来感觉晦涩难懂。

笔者撰写的《剖析 Vue.js 内部运行机制》或许可以帮到你。

关于作者

作者: 染陌

Email:[email protected]

Github: https://github.com/answershuto

知乎:https://www.zhihu.com/people/cao-yang-49/activities

掘金:https://juejin.im/user/58f87ae844d9040069ca7507

对内容有任何疑问,欢迎联系我。

learnvue's People

Contributors

answershuto avatar bodyno avatar boseny avatar caikan avatar chinanf-boy avatar hardworkingsheep avatar heruns avatar lanceli avatar lasyislazy avatar lazzzis avatar liyangworld avatar lzldemo avatar manyuemeiquqi avatar pangxie1991 avatar plortinus avatar richardsleet avatar showonne avatar softkaikai avatar spemoon avatar styx11 avatar xnnnn avatar zhe-he avatar zjh9rondo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

learnvue's Issues

【依赖收集】新建Watcher位置可能有误

大佬好!在开始依赖收集章节顶部,新建Watcher的步骤被放在了Vue类的constructor里。然而在查阅源码后,我发现Watcher是由对应平台(Web或Weex)通过在最上层Vue实例的$mount函数中调用mountComponent而新建的。请问我的理解是否正确?谢谢!

关于__ob__属性会无限循环的疑问

ob = new Observer(value);//创建ob观察者实例

var Observer = function Observer (value) {
  this.value = value;
  this.dep = new Dep();//创建发布者实例
  this.vmCount = 0;
  def(value, '__ob__', this);//将ob实例挂到__ob__属性上,这里会无限循环下去
  if (Array.isArray(value)) {
    var augment = hasProto
      ? protoAugment
      : copyAugment;
    augment(value, arrayMethods, arrayKeys);
    this.observeArray(value);
  } else {
    this.walk(value);
  }
};

请问您发现了这儿会无限循环吗,为什么要这样做呢

$emit的时候,为什么要将类数组换成数组?哪里有类数组?不都是vue自己建的吗

Vue.prototype.$emit = function (event: string): Component {
    const vm: Component = this
    if (process.env.NODE_ENV !== 'production') {
      const lowerCaseEvent = event.toLowerCase()
      if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
        tip(
          `Event "${lowerCaseEvent}" is emitted in component ` +
          `${formatComponentName(vm)} but the handler is registered for "${event}". ` +
          `Note that HTML attributes are case-insensitive and you cannot use ` +
          `v-on to listen to camelCase events when using in-DOM templates. ` +
          `You should probably use "${hyphenate(event)}" instead of "${event}".`
        )
      }
    }
    let cbs = vm._events[event]
    if (cbs) {
      /*将类数组的对象转换成数组*/
      cbs = cbs.length > 1 ? toArray(cbs) : cbs
      const args = toArray(arguments, 1)
      /*遍历执行*/
      for (let i = 0, l = cbs.length; i < l; i++) {
        cbs[i].apply(vm, args)
      }
    }
    return vm
  }

$emit的时候,为什么要将类数组换成数组?哪里有类数组?什么情况下会是类数组?我看就是$on的时候Vue自己建的数组啊

你好,子组件向父组件传值这里有个疑问

2018-12-12 12 58 10

这里是指使用v-model的情况下可以修改,还是指所有引用类型的props,修改子组件的props可以修改父组件,目前我试了只有当v-model绑定的时候,修改props才会修改父组件中的值,希望解答一下,谢谢!

对于defineReactive中的childOb我并没有理解清楚

defineReactive中的childOb我的理解好像并不是子元素,虽然这里调用会完成子元素的监听
childOb就是当前监听元素的Observer的实例引用 dep里面记录的和当前监听的元素应该一样 我觉得这里只是为了记录Object类型的元素的订阅(我还没发现有什么实际的作用)

good

这个可以用es6的 Proxy 实现

VirtualDOM与diff(Vue实现)

updateChildren部分,倒数第二个图有错误,那3个新Vnode, 应该插到 oldStartIdx前面,不是放到后面

src\core\vdom\create-element.js createElement函数

您好
请问 src\core\vdom\create-element.js createElement函数里
if (Array.isArray(data) || isPrimitive(data)) {
debugger
normalizationType = children
children = data
data = undefined
}
我不懂为什么要这么做,还希望能够指点一下,万分感谢!

defineReactive函数第一行

function defineReactive (obj, key, val, cb) {
    let val = val
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: ()=>{
            /*....依赖收集等....*/
            /*Github:https://github.com/answershuto*/
            return val
        },
        set:newVal=> {
            val = newVal;
            cb();/*订阅者收到消息的回调*/
        }
    })
}

注释问题

Dep.target = null
/依赖收集完需要将Dep.target设为null,防止后面重复添加依赖。/

这里应该只是定义吧,不是依赖收集完将Dep.target设为null?

关于依赖收集的理解问题

Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: ()=>{
            if (Dep.target) {
                /*Watcher对象存在全局的Dep.target中*/
                dep.addSub(Dep.target);
            }
        },
        set:newVal=> {
            /*只有之前addSub中的函数才会触发*/
            dep.notify();
        }
})

我这么理解的:关于大佬所说的例子中的如果text3改变了会不会触发视图更新问题关键点在于是不是触发get 方法
还有就是 这个watch中有一个问题如果是三个数据都会触发视图更新的话 会触发三次get方法也就是说这时Dep.target都会有值而在这里没有对subs判断去重会不会有重复的watcher实例存在?这些疑问还望大佬能帮我解惑答疑 @answershuto

关于响应式原理一章中的小疑问.

在响应式原理当中,需要将 _proxy(options.data);/*构造函数中*/写在构造函数当中,

function observer(value, cb) {
  Object.keys(value).forEach(key => defineReactive(value, key, value[key], cb));
}

function defineReactive(obj, key, val, cb) {
  Object.defineProperty(obj, key, {
    enumerable: true, 
    configurable: true,
    get: () => {
      /*....依赖收集等....*/
      /*Github:https://github.com/answershuto*/
    },
    set: newVal => {
      cb(); /*订阅者收到消息的回调*/
    }
  });
}

function _proxy(data) {
  const that = this;
  console.log(that);//====>在chrome下输出window?
  Object.keys(data).forEach(key => {
    Object.defineProperty(that, key, {
      configurable: true,
      enumerable: true,
      get: function proxyGetter() {
        return that._data[key];
      },
      set: function proxySetter(val) {
        that._data[key] = val;
      }
    })
  });
}

class Vue {
  constructor(options) {
    this._data = options.data;
    console.log(this) //===> app
    _proxy(options.data);/*构造函数中*/
    observer(this._data, options.render);
  }
}

let app = new Vue({
  el: '#app',
  data: {
    text: 'text',
    text2: 'text2'
  },
  render() {
    console.log('render');
  }
});

为啥在function _proxy中输出的this指向了window?

我自己修改了下,为_proxy传入了指定的context,输出才正确

function observer(value, cb) {
  Object.keys(value).forEach(key => defineReactive(value, key, value[key], cb));
}


function defineReactive(obj, key, val, cb) {
  Object.defineProperty(obj, key, {
    enumerable: true, // 当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。
    configurable: true, // 当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。
    get: () => {
      /*....依赖收集等....*/
      /*Github:https://github.com/answershuto*/
    },
    set: newVal => {
      cb(); /*订阅者收到消息的回调*/
    }
  });
}

function _proxy(data,context) {
  const that = context;
  console.log(that);//====>app
  Object.keys(data).forEach(key => {
    Object.defineProperty(that, key, {
      configurable: true,
      enumerable: true,
      get: function proxyGetter() {
        return that._data[key];
      },
      set: function proxySetter(val) {
        that._data[key] = val;
      }
    })
  });
}

class Vue {
  constructor(options) {
    this._data = options.data;
    _proxy(options.data,this);/*构造函数中*/
    observer(this._data, options.render);
  }
}

let app = new Vue({
  el: '#app',
  data: {
    text: 'text',
    text2: 'text2'
  },
  render() {
    console.log('render');
  }
});

《依赖收集》有一处表述错误

everlose指出《依赖收集》中有一处表述错误。

订阅者,当依赖收集的时候会addSub到sub中,在修改data中数据的时候会触发Watcher的notify,从而回调渲染函数。

正则compile模块没理解

比如const qnameCapture = '((?:' + ncname + '\:)?' + ncname + ')' const ncname = '[a‐zA‐Z_][\w\‐\.]*'; 这样的正则为什么就可以匹配到标签了呢 那一整章节后面都不大理解,痛苦

当data被observe后,在new render watch时触发watch的get求值,调用到pushTarget后,怎么就将这个watch添加到了subs中了?

模版:

{{test.a}}

new Vue({
data: {
test: {a: 1}
},
el: "#app"
})

我通过浏览器断点逐行调试发现,在data被observe后,此时data下的__ob__.dep.subs集合为空数组,当执行Vue.prototype.$mount时new Watcher(),得到render watch调用watch.get()求值,这个get函数第一句pushTarget(watch)这一步后立马可以看到data下的__ob__.dep.subs下添加了这个render watch,而vuejs里watch是通过addSub添加到subs的,但此时并没有发现调用dep的addSub方法,关键的是这个时候没有到render那里去,所以依赖收集无从谈起,请教这个如何做到的?

模拟代码错误

开始依赖收集
class Vue { constructor(options) { this._data = options.data; observer(this._data, options.render); let watcher = new Watcher(this, ); } }

  1. watcher的定义应该在observer之前,否则,defineReactive 无法获被添加到watcher
  2. watcher定义缺少参数

你好,

/*如果是数组的时候,则递归$on,为每一个成员都绑定上方法*/

你好,我在看你写的这个项目,学习了很多,不过这句注释我觉得应该"如果传入的事件是一个数组,就递归$on,把每个数组中的元素都绑定到当前的目标上吧",如果我理解的有错误,请忽略我~~~

依赖收集的问题

const dep = new Dep();

Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: ()=>{
        if (Dep.target) {
            dep.addSub(Dep.target);  
        }
    },
    set:newVal=> {
        dep.notify();
    }
})

**Dep.target 是全局的watcher, 假如options.data = {name:'demo', age:12}, 对它进行绑定的话,dep会两次把全局的Watcher push到自己的subs中,这样重复做有什么意义?或者理解有问题? **

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.