8.最俗学习之-Vue源码学习-数据篇(下)

源码地址

new Watcher(vm, expOrFn, cb, options),对于这个对应的文件在src/observer/watcher.js
关于这个也看了很多的文章,自己也有写了学习的笔记,不过最后还是决定引用一篇文章,因为大概
的思路也就是这样子,然后再Vue的实现里面还有很多复杂的东西,我也没怎么看懂,但是那些都是一
些辅助的东西,并不是主要的核心功能,看完下面这篇文章即可明白dep和watch和observer的关系

看这里

看这里

看这里

深入浅出Vue基于“依赖收集”的响应式原理

之前还有几个问题剩下的,今天把它看一下

Vue.set = set // 涉及到Vue的数据响应式系统,先保留

Vue.delete = del // 涉及到Vue的数据响应式系统,先保留



<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Vue.set和Vue.deletetitle>
head>
<body>
  <div id="app">
    <h2>{{msg}}h2>
    <p v-for="v in list">{{v}}p>
    <button v-text="'click me'" @click="changeList()">button>
    <button v-text="'click me'" @click="deleteList()">button>
  div>
body>
<script type="text/javascript" src="vue.js">script>
<script>

/**
 * Set a property on an object. Adds the new property and
 * triggers change notification if the property doesn't
 * already exist.
 */
function set$1 (obj, key, val) {
  if (Array.isArray(obj)) {  // 如果是数组,则使用splice方法,简单粗暴
    obj.length = Math.max(obj.length, key);
    obj.splice(key, 1, val);
    return val
  }
  if (hasOwn(obj, key)) {  // 如果是obj并且有这个key,直接赋值
    obj[key] = val;
    return
  }
  var ob = obj.__ob__;  // 获取这个obj的__ob__对象,这个也就是之前说的,经过observer之后会有这个东东挂载在上面
  if (obj._isVue || (ob && ob.vmCount)) {  // 一些操作上的判断
    "development" !== 'production' && warn(
      'Avoid adding reactive properties to a Vue instance or its root $data ' +
      'at runtime - declare it upfront in the data option.'
    );
    return
  }
  if (!ob) {  // 如果有这个ob,那么则是被observer过的,直接赋值
    obj[key] = val;
    return
  }
  defineReactive$$1(ob.value, key, val);  // 对这个值进行observer
  ob.dep.notify();   // 触发dep的notify方法,就是对应的计算属性和watch的数据更新
  return val
}

/**
 * Delete a property and trigger change if necessary.
 */
function del (obj, key) {
  var ob = obj.__ob__;
  if (obj._isVue || (ob && ob.vmCount)) {
    "development" !== 'production' && warn(
      'Avoid deleting properties on a Vue instance or its root $data ' +
      '- just set it to null.'
    );
    return
  }
  if (!hasOwn(obj, key)) {  // 如果没有则返回
    return
  }
  delete obj[key];  // 删除这个key
  if (!ob) {  // 没有ob过的则返回,不需要下面的notify了
    return
  }
  ob.dep.notify();
}


// ------------上面是这两个方法对应的源码,这里用的是构建后的源码---------------

/*

vm.$set( target, key, value )

参数:

{Object | Array} target
{string | number} key
{any} value
返回值:设置的值。

用法:

这是全局 Vue.set 的别名。

参考:Vue.set

vm.$delete( target, key )

参数:

{Object | Array} target
{string | number} key
用法:

这是全局 Vue.delete 的别名。

*/

// -----------------上面是官方文档的api的使用说明----------------------------


var vm = new Vue({
  el: '#app',
  data () {
    return {
      msg: 'hello Vue',
      list: [1,2,3,4,5]
    }
  },
  created () {

  },
  methods: {
    changeList () {
      // this.list[2] = 999;    // 用这种方法是不会更新视图的
      Vue.set(this.list, 2, 999)    // 用这种方法ok
    },
    deleteList () {
      // this.list[2] = null;    // 用这种方法是不会更新视图的
      Vue.delete(this.list, 2)    // 用这种方法ok
    }
    // 在2.1.7版本的Vue中,这两个方法会有一个bug,不过经过测试,在最新版本(2.5.13)中
    // 已经修复了,这里就不说出来咯,对应的方法在上面
  }
})
script>
html>

然后到了这个问题:initExtend(Vue),对应的源码在src/core/global-api/extend.js


// 这里我们用官网的例子来说


"en">

  "UTF-8">
  Vue.extend


  
"mount-point">
// --------------下面是方法对应的源码----------------------------------- export function initExtend (Vue: GlobalAPI) { /** * Each instance constructor, including Vue, has a unique * cid. This enables us to create wrapped "child * constructors" for prototypal inheritance and cache them. */ Vue.cid = 0 let cid = 1 /** * Class inheritance */ Vue.extend = function (extendOptions: Object): Function { extendOptions = extendOptions || {} const Super = this // 重点,这个this是Vue的构造函数 const SuperId = Super.cid // 一个编号 const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}) // 缓存的值 if (cachedCtors[SuperId]) { // 是否有缓存过的 return cachedCtors[SuperId] } const name = extendOptions.name || Super.options.name // 获取name,不是重点 if (process.env.NODE_ENV !== 'production') { // 一些操作的判断和提示 if (!/^[a-zA-Z][\w-]*$/.test(name)) { warn( 'Invalid component name: "' + name + '". Component names ' + 'can only contain alphanumeric characters and the hyphen, ' + 'and must start with a letter.' ) } } const Sub = function VueComponent (options) { // 这个Sub就是最后返回的方法 this._init(options) // 就是我们初始化的Vue的步骤,很熟悉的东西 } Sub.prototype = Object.create(Super.prototype) // 以Vue的构造函数为原型的原型 Sub.prototype.constructor = Sub // 自身的constructor Sub.cid = cid++ // 静态属性cid,一个编号 Sub.options = mergeOptions( // 合并,很熟悉的东西,就是我们最开始new Vue的时候数据合并的步骤 Super.options, extendOptions ) Sub['super'] = Super // 获取Super上面的东西 // allow further extension/mixin/plugin usage Sub.extend = Super.extend // 同理,获取Super上面的东西 Sub.mixin = Super.mixin // 同理,获取Super上面的东西 Sub.use = Super.use // 同理,获取Super上面的东西 // create asset registers, so extended classes // can have their private assets too. config._assetTypes.forEach(function (type) { // 这个也很熟悉,组件注册,过滤器,自定义指令的赋值 Sub[type] = Super[type] }) // enable recursive self-lookup if (name) { Sub.options.components[name] = Sub } // keep a reference to the super options at extension time. // later at instantiation we can check if Super's options have // been updated. Sub.superOptions = Super.options 获取各自的options Sub.extendOptions = extendOptions 获取各自的options // cache constructor cachedCtors[SuperId] = Sub 缓存下来 return Sub // 返回这个构造函数 } } // 看到了这里,有种感觉就是这个东西把Vue上面的东西都继承下来了,最后返回它,然后new它又 // 调用了Vue的init方法初始化,好吧,懵逼,鉴于官方文档也没说的太详细,这个暂时就只能这么 // 理解了,不过这个应该还有更大的用处的

到了这里大概就剩下渲染的事情了,也就是initRender方法


initLifecycle(vm)
initEvents(vm)
callHook(vm, 'beforeCreate')
initState(vm)
callHook(vm, 'created')
initRender(vm)


主要就是执行vm.$mount方法,这里大概涉及的有:

模板的编译,
生成ast,
生成render,
生成Virtual DOM,
通过snabbdom的方法实现优化,

等等,就是Vue的渲染的操作了

剩下的几个问题


Vue.nextTick = util.nextTick        // 水平有限,看不懂 - -#,dom的异步更新的问题

extend(Vue.options.directives, platformDirectives)  // 水平有限,看不懂 - -#,内置的指令directives,v-bind,v-model,v-show
extend(Vue.options.components, platformComponents)  // 水平有限,看不懂 - -#,内置的组件,transition组件
Vue.prototype.__patch__                             // 水平有限,看不懂 - -#,渲染的方法
compileToFunctions                                  // 水平有限,看不懂 - -#,模板编译的方法


const extendsFrom = child.extends                   // 水平有限,看不懂 - -#

initProxy(vm)                                       // 水平有限,看不懂 - -#

你可能感兴趣的:(vue)