- 阅读源码因为flow会报红
- 配置 setting.json中的 "javascript.validate.enable": false
- 解析到泛型时以下代码没有语法高亮
- 下载
babel-javascript
插件
- 下载
首次渲染过程
Vue
初始化,实例成员,静态成员new Vue()
this._init()
vm.$mount()
src/platforms/web/entry-runtime-with-compiler.js
- 如果没有传递
render
,把模板编译成render
函数 compileToFunctions()
生成render()
渲染函数options.render = render
vm.$mount()
src/platforms/web/runtime/index.js
mountComponent()
mountComponent(this.el)
src/core/instance/lifecycle.js
- 判断是否有
render
选项,如果没有但是传入了模板,并且当前是开发环境的话会发送警告 - 触发
beforeMount
- 定义
updateComponent
- 创建
Watcher
实例 - 触发
mounted
return vm
watcher.get()
- 创建完
watcher
会调用一次get
- 调用
updateComponent()
- 调用
vm._render()
创建VNode
- 调用
render.call(vm._renderProxy, vm.$createElement)
- 调用实例化时
Vue
传入的render()
- 或者编译
template
生成的render()
- 返回
VNode
- 调用
- 调用
vm._update(vnode, ...)
-
调用
vm.__patch__(vm.$el, vnode)
挂载真实DOM
-
记录
vm.$el
-
- 创建完
数据响应式原理
通过查看源码解决下面问题
vm.msg = { count: 0 } // 重新给属性赋值,是否是响应式的?
vm.arr[0] = 4, // 给数组元素赋值,视图是否会更新?
vm.arr.length = 0, // 修改数组的 length,视图是否会更新?
vm.arr.push(4), // 视图是否会更新
响应式处理的入口
整个响应式处理的过程是比较复杂的
src/core/instance/init.js
initState(vm)
vm
状态的初始化- 初始化了
_data
、_props
、methods
等
src/core/instance/state.js
// 数据初始化
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
针对数组的特殊处理
动态添加一个响应式属性以及相关类似方法
- Vue.set | vm.$set
- Vue.delete | vm.$delete
watch
vm.$watch
vm.$watch(expOrFn, callback, [options])
- 功能
- 观察
Vue
实例变化 的一个表达式或计算属性函数,回调函数得到的参数为新值和旧值,表达式只接受监督的键路径。对于更复杂的表达式,用一个函数取代。
- 观察
- 参数
expOrFn
:要监视的$data
中的属性,可以是表达式或函数callback
:数据变化后执行的函数- 函数:回调函数
- 对象:具有
handler
属性(字符串或者函数,如果该属性为字符串则methods
中相应的定义
options
:可选的选项deep
: 布尔类型,深度监听immediate
:布尔类型,是否立即执行一次回调函数
// https://cn.vuejs.org/v2/api/#vm-watch
var vm = new Vue({
el: '#app',
data: {
a: '1',
b: '2',
msg: 'Hello Vue',
user: {
firstName: '诸葛',
lastName: '亮'
}
}
})
// expOrFn 是表达式
vm.$watch('msg', function (newVal, oldVal) {
// ...
})
三种类型的 Watcher
- 没有静态方法,因为
$watch
方法中要使用Vue
的实例 Watcher
分三种:计算属性 Watcher
、用户Watcher(侦听器)
、渲染Watcher
- 创建顺序:
计算属性 Watcher
、用户Watcher(侦听器)
、渲染Watcher
- 创建顺序:
vm.$watch()
src/core/instance/state.js
nextTick
异步更新队列 - nextTick()
Vue
更新DOM
是异步执行的,指的- 在下次
DOM
更新循环结束之后执行延迟回调,在修改数据之后立即 使用这个方法,获取更新后的DOM
- 在下次
vm.$nextTicik(function() {// 操作 dom})
或者Vue.nexttick(function() {})
vm.$nextTick()
代码演示
<div id="app">
<p ref="p1">{{ msg }}</p>
</div>
var vm = new Vue({
el: "#app",
data: {
msg: "Hello nextTick",
name: "vue.js",
title: 'title'
},
mounted() {
this.msg = "hello world";
this.name = "Hello snabbdom"
this.title = "Vue.js"
this.$nextTick(() => {
console.log(this.$refs.p1.textContent)
})
}
})
源码
- src/core/instance/render.js
Vue.prototype.$nextTick = function(fn: Function) {
return nextTick(fn, this)
}
- 手动调用
vm.$nextTick()
- 在
Watcher
的queueWatcher
中执行nextTick()
src/core/util/next-tick.js
// 需要注意看看 timerFunc 的实现
function nextTick (cb?: Function, ctx?: Object) {
let _resolve
// 把 cb 加上异常处理存入 callbacks 数组中
callbacks.push(() => {
if (cb) {
try {
// 调用 cb()
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') {
// 返回 promise 对象
return new Promise(resolve => {
_resolve = resolve
})
}
}
响应式原理总结
Watcher
vm.$watch
vm.$watch(expOrFn, callback, [options])
- 功能
- 观察
Vue
实例变化 的一个表达式或计算属性函数,回调函数得到的参数为新值和旧值,表达式只接受监督的键路径。对于更复杂的表达式,用一个函数取代。
- 观察
- 参数
expOrFn
:要监视的$data
中的属性,可以是表达式或函数callback
:数据变化后执行的函数- 函数:回调函数
- 对象:具有
handler
属性(字符串或者函数,如果该属性为字符串则methods
中相应的定义
options
:可选的选项deep
: 布尔类型,深度监听immediate
:布尔类型,是否立即执行一次回调函数
// https://cn.vuejs.org/v2/api/#vm-watch
var vm = new Vue({
el: '#app',
data: {
a: '1',
b: '2',
msg: 'Hello Vue',
user: {
firstName: '诸葛',
lastName: '亮'
}
}
})
// expOrFn 是表达式
vm.$watch('msg', function (newVal, oldVal) {
// ...
})