<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div class="content">
<div class="box" data-id="34" @click="test12" v-if="show">{{msg}}</div>
<div>{{age}}</div>
<div>{{_msg}}</div>
</div>
</div>
<div onclick="handlerT()">
点击
</div>
<script type="text/javascript">
(function (w) {
class Observer {
constructor(data) {
setConstantProperty(data, '__ob__', this)
this.dep = new Dep()
if (isArray(data)) {
data.__proto__ = newArrMethods
this.observeArr(data)
} else {
this.walk(data)
}
}
walk(data) {
const keys = Object.keys(data)
keys.map((key) => {
defineReactive(data, key, data[key])
})
}
observeArr(data) {
data.map((item) => {
observe(item)
})
}
}
function setConstantProperty(data, key, value) {
Object.defineProperty(data, key, {
enumerable: false,
configurable: false,
value
})
}
function defineReactive(data, key, value) {
let childOb = observe(value)
let dep = new Dep()
Object.defineProperty(data, key, {
get() {
dep.depend()
return value
},
set(newValue) {
if (value === newValue) return
value = newValue
dep.notify()
}
})
}
function observe(data) {
if (!isObject(data) || data.__ob__) {
return data
}
return new Observer(data)
}
class Dep {
constructor() {
this.subs = []
}
addSub(watcher) {
this.subs.push(watcher)
}
depend() {
if (Dep.target) {
Dep.target.addDep(this)
}
}
notify() {
this.subs.forEach((watcher) => {//依次执行回调函数
watcher.update()
})
}
}
let watcherId = 0
let watcherQueue = []
let targetStack = []
class Watcher {
constructor(vm, exp, cb, options = {}, render = false) {
this.dirty = this.lazy = !!options.lazy
this.vm = vm
this.exp = exp
this.cb = cb
this.id = ++watcherId
this.deps = []
if (!this.lazy) {
this.value = this.get()
render && this.update()
}
}
addDep(dep) {
//dep实例有可能被收集过,如果收集过,则直接返回
if (this.deps.indexOf(dep) !== -1) {
return
}
this.deps.push(dep)
dep.addSub(this)
}
get() {
targetStack.push(this)
// console.log(this, 'get')
Dep.target = this
if (typeof this.exp === 'function') {
this.value = this.exp.call(this.vm)
//console.log(this.value, "watcher")
}else if(typeof this.exp === 'object'){
this.value = this.vm.noop
}else {
this.value = this.vm[this.exp]
}
targetStack.pop()
//console.log(targetStack, 'get')
if (targetStack.length > 0) {
//将栈顶的watcher拿出来放到“舞台”
Dep.target = targetStack[targetStack.length - 1]
} else {
Dep.target = null
}
}
update() {
if (this.lazy) {
this.dirty = true
} else {
this.run()
}
}
run() {
if (watcherQueue.indexOf(this.id) !== -1) { //已经存在于队列中
return
}
watcherQueue.push(this.id)
Promise.resolve().then(() => {
this.get()
this.cb.call(this.vm)
let index = watcherQueue.indexOf(this.id)
watcherQueue.splice(index, 1)
})
}
}
//======工具====================
function isObject(value) {
return typeof value === 'object' && value !== null
}
function isArray(value) {
return Array.isArray(value)
}
function query(el) {
return document.querySelector(el)
}
//======mina====================
function jui(el, options) {
this.el = el
this.$options = options
this._init()
}
init$(jui)
init2$(jui)
initReader(jui)
function init$(jui) {
jui.prototype._init = function () {
this.initState()
this.initComputed()
this.initWatch()
this.mount()
}
}
function init2$(jui) {
jui.prototype.initState = function () {
var vm = this
let data = vm.$options.data
vm._data = data = typeof data === 'function' ? data.call(vm) : data
data['noop'] = "l.0"
for (let key in data) {
proxy(vm, '_data', key)
}
observe(data)
}
jui.prototype.initWatch = function () {
let watch = this.$options.watch
if (watch) {
let keys = Object.keys(watch)
for (let i = 0
new Watcher(this, keys[i], watch[keys[i]],{}, true)
}
}
}
jui.prototype.initComputed = function () {
let computed = this.$options.computed
if (!computed) return
let keys = Object.keys(computed)
for (let i = 0
const watcher = new Watcher(this, computed[keys[i]], function () { }, {
lazy: true
})
// console.log(keys[i])
Object.defineProperty(this, keys[i], {
enumerable: true,
configurable: true,
get: function computedGetter() {
//1号watcher lazy watcher 计算属性
// watcher.dirty = true
if (watcher.dirty) {
watcher.get()
watcher.dirty = false
}
//person已经收集了 1号watcher,此时1号watcher也记录了person的dep
//watcher.get()
//watcher.exp.call(watcher.vm)
if (Dep.target) {
//console.log(watcher.deps)
//1号watcher收集到的dep,把这些dep一个个拿出来通知他们收集,现在仍然在台上的2号watcher
for (let j = 0
watcher.deps[j].depend()
}
}
//console.log(watcher.value, "computed")
return watcher.value
},
set: function computedSetter() {
console.warn('不要给计算属性赋值')
}
})
}
}
function proxy(vm, target, key) {
Object.defineProperty(vm, key, {
get() {
return vm[target][key]
},
set(newValue) {
if (vm[target][key] === newValue) return
vm[target]["noop"] = newValue + Date.parse(new Date())
vm[target][key] = newValue
}
})
}
}
function initReader(jui) {
jui.prototype.mount = function () {
// var reder = compileToRenderFunction(el.outerHTML)
var vm = this
new Watcher(vm, {}, function (value) {
updateHtml(vm)
}, {}, true)
}
let _template = ""
function updateHtml(vm) {
const reg_double_bracket = /\{\{(.*?)\}\}/g
if (!_template) _template = query(vm.el).innerHTML
query(vm.el).innerHTML = replaceVar(_template, vm, reg_double_bracket)
}
function replaceVar(html, data, reg) {
return html.replace(reg, (node, key) => {
const obj = {}
key = key.trim()
return obj[key] = data[key]
})
}
function compileToRenderFunction(template) {
var ast = parse(template)
// console.log(ast)
}
function parse(html) {
// id="app" id='app' id=app
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
//标签名 <my-header></my-header>
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`
// <my:header></my:header>
const qnameCapture = `((?:${ncname}\\:)?${ncname})`
// <div
const startTagOpen = new RegExp(`^<${qnameCapture}`)
// > />
const startTagClose = /^\s*(\/?)>/
// </div>
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)
let text,
root,
currentParent,
stack = []
let i = 1
while (html) {
let textEnd = html.indexOf('<')
//console.log(textEnd)
if (textEnd === 0) {
const startTagMatch = parseStartTag()
if (i == 2) {
console.log('startTagMat', startTagMatch)
}
if (startTagMatch) {
start(startTagMatch.tagName, startTagMatch.attrs)
continue
}
const endTagMatch = html.match(endTag)
// console.log("endTagMatch", endTagMatch)
if (endTagMatch) {
advance(endTagMatch[0].length)
end(endTagMatch[1])
continue
}
}
if (textEnd > 0) {
text = html.substring(0, textEnd)
}
if (text) {
advance(text.length)
chars(text)
}
i++
}
function start(tagName, attrs) {
const element = createASTElement(tagName, attrs)
if (!root) {
root = element
}
currentParent = element
stack.push(element)
}
function end(tagName) {
// span
//
console.log(stack)
const element = stack.pop()
// div
currentParent = stack[stack.length - 1]
if (currentParent) {
// span => parent => div
element.parent = currentParent
// div => children => push => span
currentParent.children.push(element)
}
}
function parseStartTag() {
const start = html.match(startTagOpen)
let end,
attr
if (start) {
const match = {
tagName: start[1],
attrs: []
}
advance(start[0].length)
while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {
match.attrs.push({
name: attr[1],
value: attr[3] || attr[4] || attr[5]
})
advance(attr[0].length)
}
if (end) {
advance(end[0].length)
return match
}
}
}
function advance(n) {
html = html.substring(n)
}
function createASTElement(tagName, attrs) {
return {
tag: tagName,
type: 1,
children: [],
attrs,
parent
}
}
function chars(text) {
text = text.trim()
if (text.length > 0) {
currentParent.children.push({
type: 3,
text
})
}
}
return root
}
}
w.Jui = jui
})(window)
var jui = new Jui("#app", {
computed: {
_msg: function () {
return this.msg + this.age
}
},
watch: {
_msg: function () {
console.log("watch!")
},
},
data: function () {
return {
msg: 3,
name: "Tom",
age: 18
}
}
})
let i = 1
function handlerT() {
jui.msg = ++i
jui.age = ++i
// console.log(jui)
}
//console.log("OK", jui)
</script>
</body>
</html>