指令实现过程
class Compile {
constructor(el, vue) {
this.$vue = vue //vue实例
//挂载点
this.$el = document.querySelector(el)
if(this.$el) {
// 让节点变为Fragment
let $fragment = this.node2Fragment(this.$el)
// 编译
this.compile($fragment)
// 显示
this.$el.appendChild($fragment)
}
}
node2Fragment(el) {
console.log('node2Fragment',el)
var fragment = document.createDocumentFragment();
var child;
while(child = el.firstChild) {
fragment.appendChild(child)
}
return fragment
}
compile(el) {
console.log('compile',el)
var childNodes = el.childNodes;
var self = this
var reg = /\{\{(.*)\}\}/;
childNodes.forEach(node=>{
var text = node.textContent
if(node.nodeType == 1) {
self.compileElement(node)
}if(node.nodeType == 3&®.test(text)) {
let name = text.match(reg)[1]
self.compileText(node, name)
// console.log(text)
// self.compileElement(node)
}
})
}
compileElement(node) {
var nodeAttrs = node.attributes;
console.log('nodeAttrs',nodeAttrs);
// 类数组变成数组
// Array.prototype
[].slice.call(nodeAttrs).forEach(attr=>{
var attrName = attr.name
var value = attr.value
//指令v-
var dir = attrName.substring(2)
if(attrName.indexOf('v-')== 0) {
console.log('dir',dir,value)
if(dir == 'model') {
// TODO
new Watcher(self.$vue,value,value=>{
node.value = value
})
var v = self.getVueVal(self.$vue,value)
node.value = v
node.addEventListener('input',e=>{
var newVal = e.target.value
setVueVal(self.value,value,newVal)
v = newVal
})
} else if(dir == 'if') {
} else if(dir == 'for') {
}
}
})
}
compileText(node, name) {
node.textContent = this.getVueVal(this.$vue, name)
new Watcher(this.$vue,name,value=>{
node.textContent = value
})
}
getVueVal(vue,exp) {
var val = vue
exp = exp.split('.')
exp.forEach(k=>{
val = val[k]
})
return val
}
setVueVal(vue,exp,value) {
var val = vue
exp = exp.split('.')
exp.forEach((k,i)=>{
if(i<exp.length-1){
val = val[k]
} else {
val[k] = value
}
})
}
}
class Vue {
constructor(options) {
this.$options = options || {}
this._data = options.data || undefined
// TODO 响应式数据代码
// observe(this._data)
// 默认数据变为响应式,这里就是生命周期
this._initData()
this._initWatch()
// this._initComputed()
new Compile(options.el, this)
}
_initData() {
var self = this;
Object.keys(this._data).forEach(key=>{
Object.defineProperty(self, key, {
get(){
return self._data[key]
},
set(newVal) {
self._data[key] = newVal
}
})
})
}
_initWatch() {
var self = this
var watch = this.$options.watch
Object.keys(watch).forEach(key=>{
// TODO
new Watcher(self, key, watch[key])
})
}
}
new Vue({
el: '#app',
data: {
a: 10
},
watch:{
}
})
生命周期
Vue2.0的生命周期钩子一共有10个
| 生命周期钩子 | 说明 |
|---|---|
| beforCreate | 实例初始化完成。最初调用触发,data和events都不能用。可以在这里处理整个系统加载的Loading; |
| created | 实例化已经完成之后被调用,数据观测,属性和方法的运算,watch/event事件回调已完成,挂载还没还没开始 $el属性上还没有。 |
| beforeMount | 开始挂载之前调用,相关的render函数首次被调用 此时的DOM是数据挂载前的DOM,数据还未挂载 |
| mounted | 数据已挂载后调用 初始化的el被vm.$el替换,并挂载到实例上去之后的钩子 |
| beforeUpdate | 数据更新时调用,发生在虚拟DOM重新渲染和打补丁之前 |
| updated | 在数据驱动下导致的虚拟DOM重新渲染和打补丁之后调用,此时,组件DOM已经更新 |
| activated | keep-alive 组件激活时调用 如果你使用keep-alive进行缓存, 又希望每次切换组件的时候更新数据,那么更新数据的请求方式必须写在该钩子函数里 |
| deactivated | keep-alive 组件停用时调用 |
| beforeDestroy | 实例销毁之前调用,此时实例还可用 |
| destroyed | 实例销毁后调用,实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁 |
beforeCreate -> created
- 初始化
vue实例,进行数据观测
created
- 完成数据观测,属性与方法的运算,
watch、event事件回调的配置 - 可调用
methods中的方法,访问和修改data数据触发响应式渲染dom,可通过computed和watch完成数据计算 - 此时
vm.$el并没有被创建
created -> beforeMount
- 判断是否存在
el选项,若不存在则停止编译,直到调用vm.$mount(el)才会继续编译 - 优先级:
render>template>outerHTML vm.el获取到的是挂载DOM的
beforeMount
- 在此阶段可获取到
vm.el - 此阶段
vm.el虽已完成DOM初始化,但并未挂载在el选项上
beforeMount -> mounted
- 此阶段
vm.el完成挂载,vm.$el生成的DOM替换了el选项所对应的DOM
mounted
vm.el已完成DOM的挂载与渲染,此刻打印vm.$el,发现之前的挂载点及内容已被替换成新的DOM
beforeUpdate
- 更新的数据必须是被渲染在模板上的(
el、template、render之一) - 此时
view层还未更新 - 若在
beforeUpdate中再次修改数据,不会再次触发更新方法
updated
- 完成
view层的更新 - 若在
updated中再次修改数据,会再次触发更新方法(beforeUpdate、updated)
beforeDestroy
- 实例被销毁前调用,此时实例属性与方法仍可访问
destroyed
- 完全销毁一个实例。可清理它与其它实例的连接,解绑它的全部指令及事件监听器
- 并不能清除DOM,仅仅销毁实例
Vue的挂载过程中发生了什么
-
new Vue的时候调用会调用_init方法- 定义
$set、$get、$delete、$watch等方法 - 定义
$on、$off、$emit、$off等事件 - 定义
_update、$forceUpdate、$destroy生命周期
- 定义
-
调用
$mount进行页面的挂载 -
挂载的时候主要是通过
mountComponent方法 -
定义
updateComponent更新函数 -
执行
render生成虚拟DOM -
_update将虚拟DOM生成真实DOM结构,并且渲染到页面中