Skip to content

Commit

Permalink
#43-2, computed
Browse files Browse the repository at this point in the history
  • Loading branch information
adjfks committed Dec 11, 2022
1 parent e1fd1fd commit d1ffe1d
Show file tree
Hide file tree
Showing 4 changed files with 280 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"type": "chrome",
"request": "launch",
"name": "Open index.html",
"file": "d:\\CS\\Repositories-self\\mini-code\\code\\vue-source\\index.html"
"file": "d:\\CS\\Repositories-self\\mini-code\\code\\vue-source\\4-computed\\index.html"
}
]
}
70 changes: 70 additions & 0 deletions code/vue-source/3-$refs/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<div id="app"></div>
<script src="../vue.js"></script>
<script>
const Comp = Vue.component('Comp', {
name: 'Comp',
data: function () {
return {
count: 1
}
},
template: `
<div>
<p ref="count">count: {{ count }}</p>
<button @click="increase">+1</button>
</div>
`,
methods: {
increase: function () {
this.count += 1
console.log('count: ', this.$refs.count.innerHTML);
this.$nextTick(() => {
console.log('count in nextTick: ', this.$refs.count.innerHTML);
})
}
}
})
const vm = new Vue({
el: '#app',
data: {
name: 'Joke',
age: 18,
citys: [
'BeiJin',
'ShenZhen',
'ShangHai'
]
},
template: `
<div>
<div ref="div">普通DOM元素div</div>
<Comp ref="comp"/>
<ul>
<li v-for="city in citys" id="city" ref="liArr">{{ city }}</li>
</ul>
</div>
`,
beforeMount: function () {
console.log('div', this.$refs.div);
},
mounted: function () {
console.log('div', this.$refs.div);
console.log('comp', this.$refs.comp);
console.log('liArr', this.$refs.liArr);
}
})
</script>
</body>

</html>
53 changes: 53 additions & 0 deletions code/vue-source/4-computed/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<div id="app"></div>
<script src="../vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: function () {
return {
a: 1,
b: 2,
c: 3
}
},
template: `
<div>
<p>a: {{ a }}</p>
<p>b: {{ b }}</p>
<p>c: {{ c }}</p>
<p>a * b * c {{ abc }}</p>
<button @click="changeA">a + 1</button>
<button @click="getAbc">get abc</button>
</div>
`,
computed: {
abc() {
console.log('a * b * c: ', this.a * this.b * this.c);
return this.a * this.b * this.c
}
},
methods: {
changeA() {
console.log('a plus 1');
this.a += 1
},
getAbc() {
console.log('abc: ', this.abc);
}
}
})
</script>
</body>

</html>
156 changes: 156 additions & 0 deletions code/vue-source/4-computed/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
function Mvvm(config) {
this.$el = config.el
this.$root = document.querySelector(config.el)
this.$data = observe(config.data)
// 用于将data中的属性代理到this实例上,使得可以通过this.xxx的方式访问数据
proxy.call(this, this.$data)
this.$template = config.template
new Watcher(() => {
console.log(this.name, this.age);
render.call(this)
})
if (config.computed) {
initComputed(this, config.computed)
}

function Dep() {
// 收集订阅者(依赖)的数组
this.subscribers = []
// 添加依赖的方法
this.addSub = function (sub) {
if (sub && this.subscribers.findIndex(item => item === sub) === -1) {
this.subscribers.push(sub)
}
if (sub && sub.deps.findIndex(item => item === this) === -1) {
sub.depend(this)
}
}
// 通知所有依赖更新的方法
this.notifyAll = function () {
for (var i = 0; i < this.subscribers.length; i++) {
this.subscribers[i].update()
}
}
}

function observe(obj) {
for (let key in obj) {
// 每一个属性对应一个Dep实例
let dep = new Dep()
let value = obj[key]
if (Object.prototype.toString.call(value) === '[object Object]') {
observe(value)
}
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,
get: function () {
if (Dep.target) {
// 添加依赖
dep.addSub(Dep.target)
}
return value
},
set: function (newVal) {
if (newVal === value) return value
value = newVal
dep.notifyAll()
}
})
}
return obj
}

function Watcher(fn, options) {
this.dirty = false
this.lazy = !!options.lazy
// 用于收集dep
this.deps = []
// 更新函数
this.update = function () {
// 计算属性的watcher.lazy为true
if (this.lazy) {
this.dirty = true
} else {
this.value = fn()
}
}
this.evaluate = function () {
if (this.lazy) {
this.value = fn()
}
return this.value
}

this.depend = function (dep) {
this.deps.push(dep)
}
// 初始化watcher时要给相应数据添加依赖
Dep.target = this
this.value = fn()
Dep.target = null
}

function proxy(data) {
for (let key in data) {
Object.defineProperty(this, key, {
configurable: true,
enumerable: true,
get: function () {
return this.$data[key]
},
set: function (newVal) {
this.$data[key] = newVal
}
})
}
}

// 处理template并进行渲染
function render() {
var html = this.$template
.replace(/"/g, '\\"')
.replace(/\s+|\r|\t|\n/g, ' ')
.replace(/\{\{.*?\}\}/g, function (value) {
return value.replace('{{', '"+(').replace('}}', ')+"')
})
html = `var targetHtml = "${html}";return targetHtml;`
// 语法:new Function(...args, functionBody)
var parsedHtml = new Function(...Object.keys(this.$data), html)(...Object.values(this.$data))
this.$root.innerHTML = parsedHtml
}

// 暂时只实现函数形式的computed
function initComputed(vm, computed) {
var watchers = vm._computedWatchers = Object.create(null)
for (let key in computed) {
const getter = computed[key]
watchers[key] = new Watcher(getter, { lazy: true })
// 将计算属性代理到vm实例上
if (!(key in vm)) {
defineComputed(vm, key);
}
}
}

function defineComputed(vm, key) {
Object.defineProperty(vm, key, {
enumerable: true,
configurable: true,
get: createComputedGetter(key)
})
}

function createComputedGetter(key) {
// 获取该属性对应的watcher
var watcher = this._computedWatchers && this._computedWatchers[key];
if (watcher) {
if (watcher.dirty) {
watcher.evaluate()
}
if (Dep.target) {

}
}
}
}

0 comments on commit d1ffe1d

Please sign in to comment.