Git Product home page Git Product logo

vue-analysis's Introduction

Vue.js 技术揭秘

电子书

目前社区有很多 Vue.js 的源码解析文章,但是质量层次不齐,不够系统和全面,这本电子书的目标是全方位细致深度解析 Vue.js 的实现原理,让同学们可以彻底掌握 Vue.js。目前分析的版本是 Vue.js 的最新版本 Vue.js 2.5.17-beta.0,并且之后会随着版本升级而做相应的更新,充分发挥电子书的优势。

这本电子书是作为 《Vue.js 源码揭秘》视频课程的辅助教材。电子书是开源的,同学们可以免费阅读,视频是收费的,25+小时纯干货课程,如果有需要的同学可以购买来学习,但请务必支持正版,请尊重作者的劳动成果

章节目录

为了把 Vue.js 的源码讲明白,课程设计成由浅入深,分为核心、编译、扩展、生态四个方面去讲,并拆成了八个章节,如下图:

第一章:准备工作

介绍了 Flow、Vue.js 的源码目录设计、Vue.js 的源码构建方式,以及从入口开始分析了 Vue.js 的初始化过程。

第二章:数据驱动

详细讲解了模板数据到 DOM 渲染的过程,从 new Vue 开始,分析了 mountrenderupdatepatch 等流程。

第三章:组件化

分析了组件化的实现原理,并且分析了组件周边的原理实现,包括合并配置、生命周期、组件注册、异步组件。

第四章:深入响应式原理

详细讲解了数据的变化如何驱动视图的变化,分析了响应式对象的创建,依赖收集、派发更新的实现过程,一些特殊情况的处理,并对比了计算属性和侦听属性的实现,最后分析了组件更新的过程。

第五章:编译

从编译的入口函数开始,分析了编译的三个核心流程的实现:parse -> optimize -> codegen

第六章:扩展

详细讲解了 eventv-modelslotkeep-alivetransitiontransition-group 等常用功能的原理实现,该章节作为一个可扩展章节,未来会分析更多 Vue 提供的特性。

第七章:Vue-Router

分析了 Vue-Router 的实现原理,从路由注册开始,分析了路由对象、matcher,并深入分析了整个路径切换的实现过程和细节。

第八章:Vuex

分析了 Vuex 的实现原理,深入分析了它的初始化过程,常用 API 以及插件部分的实现。

vue-analysis's People

Contributors

cbczed avatar contra999 avatar dengwb1991 avatar dependabot[bot] avatar felix0814 avatar gavin-gong avatar guaoo avatar jiaoguanwen avatar kaiorange avatar lasyislazy avatar liyangworld avatar lizehongss avatar lz-lee avatar moozeeli avatar peterchen1997 avatar plortinus avatar proc07 avatar remote-star avatar ruiyong-lee avatar shellwolf avatar stanj avatar theniceangel avatar theydy avatar ulivz avatar ustbhuangyi avatar wangshun23 avatar yeyan1996 avatar yinjunjian0 avatar yuzexia avatar zsi2017 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

vue-analysis's Issues

Clerical mistakes

In chapter 'Vue 实例挂载的实现' with line 7,the file path describe 'src/platform/web/entry-runtime-with-compiler.js' should be 'src/platforms/web/entry-runtime-with-compiler.js'.

It's a little mistake ofcourse.

v2.6.11 计算属性的实现是不是又重写了?

计算属性的最终的值 不变时 也是会重新渲染的
依赖的值发生了变化就会dep.notify() computed watcher将dirty置为true 渲染watcher还是会执行
不是说这是个优化吗 为什么又去掉了?

响应式对象里 defineReactive、proxy 的作用疑问

首先介绍一下代理,代理的作用是把 propsdata 上的属性代理到 vm 实例上,这也就是为什么比如我们定义了如下 props,却可以通过 vm 实例访问到它。

let comP = {
  props: {
    msg: 'hello'
  },
  methods: {
    say() {
      console.log(this.msg)
    }
  }
}

您在文中提到代理的作用是把 props 和 data 上的属性代理到 vm 实例上,而我在分析 defineReactive 和 proxy 方法时理解的 defineReactive 方法的作用是将每个 prop 对应的值变成响应式,并可通过 vm._props.xxx 访问到定义 props 中对应的属性;proxy 方法的作用是把 vm.xxx 的访问代理到 vm._props.xxx 上。
当访问 vm 实例时会先将 vm.xxx 的访问代理到 vm._props.xxx 上(通过 proxy 实现),而访问 vm._props.xxx 又会被代理到 vm.props.xxx上(通过 defineReactive 实现),所以当定义了上述 props 时可以通过 vm 实例访问到它是通过 defineReactive 和 proxy 方法的双重代理实现的。

与您的理解有些不相符,望您能为我解答,谢谢。

派发 更新

你好, 在 派发 更新 中,我怎么没有看到确定哪个 数据 需要 更新呢 , 求老师指教

技术探讨

看完大佬的分享,十分仰慕,大佬可否提供一个一个社交账号(例如微信),有一些技术问题想和您探讨

关于nextTick的实现

在nextTick实现里,有一段关于macrotask和microtask的执行顺序的模拟:

for (macroTask of macroTaskQueue) {
    // 1. Handle current MACRO-TASK
    handleMacroTask();
      
    // 2. Handle all MICRO-TASK
    for (microTask of microTaskQueue) {
        handleMicroTask(microTask);
    }
}

是不是将第二点提到第一点之上更合适:

for (macroTask of macroTaskQueue) {
      
    // 1. Handle all MICRO-TASK
    for (microTask of microTaskQueue) {
        handleMicroTask(microTask);
    }

   // 2. Handle current MACRO-TASK
    handleMacroTask();
}

vue2.x响应性疑惑

请教一下,我在data中定义一个空对象,在模板中使用这个对象,给输入框绑定v-model="obj.name", 他也能保证响应性,我记得之前看文档不是会先遍历data中存在的属性值,给变成响应式数据,难道在解析模板的时候也会动态给这些属性加响应性?但是如果我直接在模板中v-model一个不存在的属性它是会报错的,比如 v-model="name"

Vue原理问题讨论, 关于vue与原生js混用

代码如下:
https://jsfiddle.net/afenotes/bkjxvr07/

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>VUE</title>
	<script src="https://unpkg.com/[email protected]"></script>
	<script src="https://unpkg.com/vue"></script>
</head>
<body>
	<ul id="list">
		<li v-for="(l,index) in list" v-bind:key="index" :ref='"li"+index'>{{l}}</li>
	</ul>
	<script>
		var ul = new Vue({
			el: '#list',
			data: {
				list: ['apple','banana','orange']
			}
		})
		setTimeout(function(){
			// $('<li>grape</li>').prependTo($('#list'))
			$('#list').children().first().text('grape')
		}, 3000);
		setTimeout(function(){
			ul.list = ['Bob','Tom','Jim']
		}, 6000);
	</script>
</body>
</html>

初始渲染结果:

  • apple
  • banana
  • orange

3s后,渲染结果:

  • grape
  • banana
  • orange

6s后,实际渲染结果:

  • grape
  • Tom
  • Jim

期望结果:

  • Bob
  • Tom
  • Jim

为什么第一个元素没有更新?猜测virtual dom到真实dom之间的映射丢了,求指点。
(知道这个混用不对,好奇背后的原理)

data.hook 中的 `create` hook是哪来的

function invokeCreateHooks (vnode, insertedVnodeQueue) {
    for (let i = 0; i < cbs.create.length; ++i) {
      cbs.create[i](emptyNode, vnode)
    }
    i = vnode.data.hook // Reuse variable
    if (isDef(i)) {
      if (isDef(i.create)) i.create(emptyNode, vnode)
      if (isDef(i.insert)) insertedVnodeQueue.push(vnode)
    }
  }

core/vdom/patch 里有这个方法

我找了好久都没找到 create hook的来源,我去我的一个项目里加了断点,测了几个地方发现这个地方根本没有执行,所以i.create(emptyNode, vnode)这一句到底是什么作用?

Event Loop疑问请教

你好,我在掘金上看到一篇文章说的Event Loop是宏任务执行完成后再执行微任务, ui更新是在宏任务结束后执行。同时下面 阮一峰老师链接点进去后也是说的是这样。

但我在另一篇文章说的是先执行微任务再执行宏任务, ui更新是在微任务结束后执行。在学习您vuejs核心解密过程中了解到您,希望您有空的时候回复下

观点1:
掘金 https://juejin.im/post/5d5b4c2df265da03dd3d73e5
下面阮一峰老师链接 https://www.cnblogs.com/dailc/p/8325991.html
观点2:
掘金 https://juejin.im/post/5d57994ef265da03bd051969
下面参考链接 aooy/blog#5

Vue v2.6+ nextTick 原理已经不一样了,希望注明或更改文档

next-tick.js

/* @flow */
/* globals MutationObserver */

import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'

export let isUsingMicroTask = false

const callbacks = []
let pending = false

function flushCallbacks () {
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}

// Here we have async deferring wrappers using microtasks.
// In 2.5 we used (macro) tasks (in combination with microtasks).
// However, it has subtle problems when state is changed right before repaint
// (e.g. #6813, out-in transitions).
// Also, using (macro) tasks in event handler would cause some weird behaviors
// that cannot be circumvented (e.g. #7109, #7153, #7546, #7834, #8109).
// So we now use microtasks everywhere, again.
// A major drawback of this tradeoff is that there are some scenarios
// where microtasks have too high a priority and fire in between supposedly
// sequential events (e.g. #4521, #6690, which have workarounds)
// or even between bubbling of the same event (#6566).
let timerFunc

// The nextTick behavior leverages the microtask queue, which can be accessed
// via either native Promise.then or MutationObserver.
// MutationObserver has wider support, however it is seriously bugged in
// UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
// completely stops working after triggering a few times... so, if native
// Promise is available, we will use it:
/* istanbul ignore next, $flow-disable-line */
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  timerFunc = () => {
    p.then(flushCallbacks)
    // In problematic UIWebViews, Promise.then doesn't completely break, but
    // it can get stuck in a weird state where callbacks are pushed into the
    // microtask queue but the queue isn't being flushed, until the browser
    // needs to do some other work, e.g. handle a timer. Therefore we can
    // "force" the microtask queue to be flushed by adding an empty timer.
    if (isIOS) setTimeout(noop)
  }
  isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
  isNative(MutationObserver) ||
  // PhantomJS and iOS 7.x
  MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
  // Use MutationObserver where native Promise is not available,
  // e.g. PhantomJS, iOS7, Android 4.4
  // (#6466 MutationObserver is unreliable in IE11)
  let counter = 1
  const observer = new MutationObserver(flushCallbacks)
  const textNode = document.createTextNode(String(counter))
  observer.observe(textNode, {
    characterData: true
  })
  timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  // Fallback to setImmediate.
  // Techinically it leverages the (macro) task queue,
  // but it is still a better choice than setTimeout.
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else {
  // Fallback to setTimeout.
  timerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}

export function nextTick (cb?: Function, ctx?: Object) {
  let _resolve
  callbacks.push(() => {
    if (cb) {
      try {
        cb.call(ctx)
      } catch (e) {
        handleError(e, ctx, 'nextTick')
      }
    } else if (_resolve) {
      _resolve(ctx)
    }
  })
  if (!pending) {
    pending = true
    timerFunc()
  }
  // $flow-disable-line
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}

为什么要在watcher.run再检查一次value!==watcher.value?

你好,我阅读vue的源码,发觉在setter已经做了次差值比较,watcher也经过了去重处理,然后却又再watcher.run做了一次新旧值比较(只比对primitive类型),才决定是否执行cb回调。那这个比较会在什么情景下会有用?还是其实基本是多余的?
麻烦指点一下,谢谢

slot

image
你好,我想问一下这个作用域插槽的作用域是子组件实例的问题。
据我所了解的是函数的作用域是词法作用域,所以作用域在函数定义的那个时候就已经定了,因为函数是在父组件中定义的,我认为对应的数据作用域还是父组件的作用域,不知道对不对呢?希望赐教,谢谢

关于 vm.$vnode 书中的说法似乎有误。

  // manually mounted instance, call mounted on self
  // mounted is called for render-created child components in its inserted hook
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }

 这里注意 vm.$vnode 表示 Vue 实例的父虚拟 Node,所以它为 Null 则表示当前是根 Vue 的实例。

vm.$vnode 应该就是自己的vnode吧,mount的时候第一次调用 updateComponent ,此时 $vnode 是 null,要触发一下 mounted hook 的回调 。如果是数据变化导致的 mountComponent ,则$vnode 就不是 null 了,而是上一次render 的 vnode.
不知道我理解的对不对。

Vue如何处理计算属性中不需要使用的依赖

计算属性
a(){
const b = this.b;
return 1;
}
逻辑上a对b是没有依赖关系的, 但是Vue能分辨吗? getter执行时一样会进入b的getter并添加依赖呀.
Vue内部有这部分处理的相关代码吗?

源码stateData 中 getData 函数的一段代码 data.call(vm, vm)

export function getData (data: Function, vm: Component): any {
// #7573 disable dep collection when invoking data getters
pushTarget()
try {
return data.call(vm, vm) // 为什么还要传一个vm的参数呢
} catch (e) {
handleError(e, vm, data())
return {}
} finally {
popTarget()
}
}

关于给数据添加__ob__属性的疑惑

  • 你好,最近在研究vue源码过程中看了你的电子书,有个疑惑请教一下
    源码
    image

执行上述红框代码前的value
image
执行上述红框代码后的value
image

这里的__ob__好像是无穷无尽循环下去了,请问这样不会有问题么?

compileToFunctions 的疑问

编译入口章节提到了 compileToFunctions。在 vue-template-compiler 也包含 compileToFunctions,这两个应该都是同一个函数吧。

我希望通过把调用该函数把 template 转成 render 函数。

compiler.compileToFunctions('<div>x</div>').render.toString()
// function anonymous() {\nwith(this){return _c(\'div\',{},[_v("x")])}\n}

我期望的返回值是:render 函数中不要包含 with 语句,就像 vue-loader 处理后生成的 render 函数。

var render = function() {
  var _vm = this
  var _h = _vm.$createElement
  var _c = _vm._self._c || _h
  return _c('div',{},[_vm.v("x")])
}

请问该如何做到这一步?

笔误

keep-alive 生命周期
原文:这里判断如果是被<keep-alive> 包裹的组件已经 mounted,那么则执行 queueActivatedComponent(componentInstance)。
建议:‘被<keep-alive> 包裹的组件已经 mounted ’ 应该改为 ‘<keep-alive> 组件的父组件已经 mounted’。毕竟上面那条 if 语句的作用就是调用 '被<keep-alive> 包裹的组件' 的 mounted 钩子。

关于Vue构造函数没有使用ES6的Class语法

在Vue构造函数那段说,不用class是方便Vue按功能分散到多个模块对prototype进行扩展,而class难以实现,这里不太明白。class应该只是语法糖,对class的prototype也是可以扩展的,而且效果一样。不明白有何区别,还请老师指点

Vuex-API-数据获取中示例代码错误

大佬您好,看您文章Vuex-API-数据获取中示例代码中,有一处

getters: {
  total (state, getters, localState, localGetters) {
    // 可访问全局 state 和 getters,以及如果是在 modules 下面,可以访问到局部 state 和 局部 getters
    return state.a + state.b
  }
}

这个地方是不是写错了?
根据源码:

 store._wrappedGetters[type] = function wrappedGetter (store) {
    return rawGetter(
      local.state, // local state
      local.getters, // local getters
      store.state, // root state
      store.getters // root getters
    )
  }

代码中total的参数有错误,应该更改为:

getters: {
  total (state, getters, rootState, rootGetters) {
    // 可访问全局 state 和 getters,以及如果是在 modules 下面,可以访问到局部 state 和 局部 getters
    return state.a + state.b
  }
}

响应式对象里proxy方法中的代理疑问

export function proxy (target: Object, sourceKey: string, key: string) {
  sharedPropertyDefinition.get = function proxyGetter () {
    return this[sourceKey][key]
  }
  sharedPropertyDefinition.set = function proxySetter (val) {
    this[sourceKey][key] = val
  }
Object.defineProperty(target, key, sharedPropertyDefinition)

通过 Object.defineProperty 把 target[sourceKey][key] 的读写变成了对 target[key] 的读写。所以对于 props 而言,对 vm._props.xxx 的读写变成了 vm.xxx 的读写,而对于 vm._props.xxx 我们可以访问到定义在 props 中的属性,所以我们就可以通过 vm.xxx 访问到定义在 props 中的 xxx 属性了

我理解的是执行Object.defineProperty(target, key, sharedPropertyDefinition)后,对target[key]的读写会变为对target[sourceKey][key]的读写,与您文中的描述恰好相反,所以对此抱有困惑,望您能为我解疑,谢谢。

nextTick v2.6.6

2.6.x 版本中,我看到 VuenextTick 又重新换成 mircoTask 的实现了, 那很好奇 2.4.x 之前版本中就是 microTask ,也存在一些问题,诸如 #4521#6690#6566 等。那这次重新回归到 microTaskVue 又是怎么解决了上述问题的,我用最新版本测试了一下,好像都是ok的。

数据驱动—vue实例挂载的实现,一节的疑问

以下是原文

从上面的代码可以看到,mountComponent 核心就是先调用 vm._render 方法先生成虚拟 Node,再实例化一个渲染Watcher,在它的回调函数中会调用 updateComponent 方法,最终调用 vm._update 更新 DOM。

我的疑问是vm._render方法是在updateComponent里面执行的吧,怎么会发生在实例化渲染watcher之前呢?

我只是发表下我的看法,我知道我不该这么提issue的

你好:
我很尊重你开源了很多的作品,这几天发生了DD前负责人的事情。Vue权威指南刚出来的时候,我在前端的一个公共号买了这本书,因为喜欢Vue,希望可以对vue有新的见解,也是对Vue的喜欢。谁知道收了这本书,我看了大部分都是Vue官网的例子和API,索然无趣。。。。我回复了公共号的人,对我说 请不要侮辱我的团队的努力什么的,原话不记得了。那个时候如果书里有你们对vue的剖析之类的,应该会很满意这本书的。只是发个唠叨。。。仅此而已==

English version

Hi, can you add English version?
It is very interesting to learn more deeply vue.js.

next-tick机制改变了

根据尤雨溪的注释,好像是因为重新绘制之前更改状态时出现问题并且在事件处理程序中使用(宏)任务会导致一些奇怪的行为,所以还是改回之前的将micro task拆分在各处使用,文章的解析说的microTimerFunc与macroTimerFunc变量的定义在源码没找到了

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.