vue源码
1、双向数据绑定
var arr = [2,4,5,67,78,5,1]
const total = arr.reduce((val,item)=>{
return val + item
}, 0)
console.log(total)
const obj ={
name: 'zs',
info: {
address: {
location: '北京大东'
}
}
}
const attrstr = 'info.address.location'
const location = attrstr.split('.').reduce((newObj, k) => newObj[k], obj)
console.log(location, 'location')
2 、vue发布订阅原理和实现
class Dep{
constructor() {
this.subs = []
}
addSub(watcher){
this.subs.push(watcher)
}
notify() {
this.subs.forEach((watcher) => watcher.update())
}
}
class Watcher {
constructor(cb) {
this.cb = cb
}
update() {
this.cb()
}
}
const w1 = new Watcher(()=>{
console.log('我是一个订阅者')
})
const w2 = new Watcher(()=>{
console.log('我是二个订阅者')
})
const dep = new Dep()
dep.addSub(w1)
dep.addSub(w2)
dep.notify()
- vue的发布订阅模式运作
- 只要我们为vue中data数据重新赋值了,这个赋值的动作,会被vue监听到
- 然后vue要把数据的变化,通知到每个订阅者
- 接下来,订阅者(DOM元素)要根据最新的数据,更新自己的内容
- 谁是订阅者
- 为什么要订阅
- 通俗易懂的说 就是当vue中的数据被重新赋值,变化后的一瞬间,就要调用notify通知每个订阅者(DOM元素)更新dom结构,当订阅者(DOM元素)刚被创建的时候就要添加到存放订阅者信息来
3、手动实现vue类 为vue添加getter setter
class Vue{
constructor(options){
this.$data = options.data
Observe(this.$data)
console.log(Object.keys(this.$data))
Object.keys(this.$data).forEach(key =>{
Object.defineProperty(this, key, {
enumerable: true,
configurable: true,
get(){
return this.$data[key]
},
set(newValue){
this.$data[key] = newValue
}
})
})
}
}
function Observe(obj) {
if(!obj || typeof obj !=='object' ) return
Object.keys(obj).forEach(key =>{
let value = obj[key]
Observe(value)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get(){
console.log("get方法被调用");
return value
},
set(newValue){
console.log("set方法被调用", newValue);
value = newValue
Observe(value)
}
});
})
}
const vm = new Vue({
el: '#app',
data:{
name: 'zs',
age: 20,
info: {
a: 'a1',
c: 'c1'
}
}
})
vm.$data.info = {d:'d1', e:'e1'}
console.log(vm.$data.info)
4、 对HTML结构进行模板编译的方法
- js性能优化,创建文档碎片,提高DOM操作性能
( createDocumentFragment作用是创建一个文档碎片,把要插入的新节点先附加在它上面,然后再一次性添加到document中)
class Vue{
constructor(options){
this.$data = options.data
Observe(this.$data)
Object.keys(this.$data).forEach(key =>{
......
})
Complile(options.el, this)
}
}
function Observe(obj) {
....
}
function Complile(el, vm) {
vm.$el = document.querySelector(el)
const fragment = document.createDocumentFragment
while(chileNode = vm.$el.firstChild){
fragment.appendChild(chileNode)
}
replace(fragment)
vm.$el.appendChild(fragment)
function replace(node) {
const regMustache =/\{\{\s*(\S+)\s*\}\}\}/
if(node.nodeType === 3){
const text = node.textContent
const exectResult = regMustache.exec(text)
if(exectResult) {
const value = exectResult[1].split('.').reduce((newObj, k) => newObj[k], vm)
node.textContent = text.replace(exectResult, value)
}
return
}
node.chileNodes.forEach(child => replace(child))
}
}
5、 发布订阅实现
class Vue{
constructor(options){
this.$data = options.data
Observe(this.$data)
console.log(Object.keys(this.$data))
Object.keys(this.$data).forEach(key =>{
Object.defineProperty(this, key, {
enumerable: true,
configurable: true,
get(){
return this.$data[key]
},
set(newValue){
this.$data[key] = newValue
}
})
})
Complile(options.el, this)
}
}
function Observe(obj) {
const dep = new Dep()
if(!obj || typeof obj !=='object' ) return
Object.keys(obj).forEach(key =>{
let value = obj[key]
Observe(value)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get(){
console.log("get方法被调用");
Dep.target && dep.addSub(Dep.target)
return value
},
set(newValue){
console.log("set方法被调用", newValue);
value = newValue
Observe(value)
dep.notify()
}
});
})
}
function Complile(el, vm) {
vm.$el = document.querySelector(el)
const fragment = document.createDocumentFragment
while(chileNode = vm.$el.firstChild){
fragment.appendChild(chileNode)
}
replace(fragment)
vm.$el.appendChild(fragment)
function replace(node) {
const regMustache =/\{\{\s*(\S+)\s*\}\}\}/
if(node.nodeType === 3){
const text = node.textContent
const exectResult = regMustache.exec(text)
if(exectResult) {
const value = exectResult[1].split('.').reduce((newObj, k) => newObj[k], vm)
node.textContent = text.replace(regMustache, value)
new Watcher(vm , exectResult[1], (newValue) =>{
node.textContent = text.replace(regMustache, newValue)
})
}
return
}
node.chileNodes.forEach(child => replace(child))
}
}
class Dep{
constructor() {
this.subs = []
}
addSub(watcher){
this.subs.push(watcher)
}
notify() {
this.subs.forEach((watcher) => watcher.update())
}
}
class Watcher {
constructor(vm,key,cb) {
this.vm = vm
this.key = key
this.cb = cb
Dep.target = this
key.split('.').reduce((newObj, k) => newObj[k], vm)
Dep.target = null
}
update() {
const value = this.key.split('.').reduce((newObj, k) => newObj[k], this.vm)
this.cb(value)
}
}
