# 效果

## html
```
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>my-vue</title>
</head>
<style>
#app {
text-align: center;
}
</style>
<body>
<div id="app">
<h2>{{title}}</h2>
<input v-model="name">
<h1>{{name}}</h1>
<button v-on:click="clickButton">click me!</button>
</div>
</body>
<script src="js/observer.js"></script>
<script src="js/watcher.js"></script>
<script src="js/compile.js"></script>
<script src="js/index.js"></script>
<script type="text/javascript">
var vue = new Vue({
el: '#app',
data: {
title: 'hello world',
name: 'canfoo'
},
methods: {
clickButton: function () {
this.title = this.name;
}
},
mounted: function () {
this.name = "這是mounted改變的"
window.setTimeout(() => {
this.title = '你好';
window.setTimeout(()=> {
this.clickButton();
},2000);
}, 1000);
}
});
</script>
</html>
```
## index.js
```
function Vue(options) {
this.data = options.data;
this.el = document.querySelector( options.el);
this.methods = options.methods;
this.options = options;
var that = this;
Object.keys(this.data).forEach((key)=> {
that.proxyKeys(key);
});
Object.keys(this.methods).forEach((method)=> {
that.proxyMethods(method,that.methods[method]);
});
observerFactory(this.data);
new Compile(this.el,that);
// 執行 mounted方法
options.mounted.call(this);
}
Vue.prototype = {
// 將劫持的數據綁定到Vue對象上面
// 實現 this.xxx 獲取 或 賦值
proxyKeys: function(key) {
var that = this;
Object.defineProperty(this, key, {
configurable: true,
enumerable: true,
get: function() {
return that.data[key];
},
set: function(newVal) {
that.data[key] = newVal;
}
})
},
// 將methods對象里的方法綁定到Vue對象上面, this.方法 調用
proxyMethods: function(method, fn) {
this[method] = fn;
}
}
```
## observer.js
```
function observerFactory(data) {
for (const property in data) {
if (data.hasOwnProperty(property)) {
addObserver(data, property, data[property]);
const val = data[property];
// 結果是數組,處理數組內置的方法
if (val && val instanceof Array) {
addFunction(data, property, val);
} else if(val && val instanceof Object) {
// 結果是對象就再次遞歸處理
observerFactory(val);
}
}
}
}
// 添加數據劫持
function addObserver(data, key, val) {
const dep = new Dep();
Object.defineProperty(data, key, {
configurable: true,
enumerable: true,
get: function() {
if (Dep.target) {
dep.addSub(Dep.target);
}
return val;
},
set: function(newVal) {
if (val !== newVal) {
val = newVal;
dep.notify();
}
}
})
}
// 處理數組中的內置方法
function addFunction(data, key, val) {
["shift","unshift","pop","push","reverse","sort"].forEach((mothod)=>{
const __o__ = Array.prototype[mothod];
val[mothod] = function() {
// 調用原來的方法,獲取執行結果
const result = __o__.apply(this, arguments);
// 添加要處理的內容
console.log("使用 "+mothod+" 方法, 返回結果為: "+__o__);
return result;
}
});
}
function Dep() {
this.subs = [];
}
Dep.prototype = {
addSub: function(sub) {
this.subs.push(sub);
},
notify: function() {
this.subs.forEach((sub)=> {
sub.update();
})
}
}
Dep.target = null;
```
## Watcher.js
```
function Watcher(vm, exp, cb) {
this.vm = vm;
this.cb = cb;
this.exp = exp;
this.value = this.get();
}
Watcher.prototype = {
update: function() {
console.log("update 方法更新了數據");
var newVal = this.vm.data[this.exp];
var oldVal = this.value;
if (oldVal !== newVal) {
this.value = newVal;
this.cb.call(this.vm,newVal);
}
},
get: function() {
Dep.target = this;
const value = this.vm.data[this.exp];
Dep.target = null;
return value;
}
}
```
## compile.js
```
function Compile(el, vm) {
this.el = el;
this.vm = vm;
this.fragment = null;
this.init();
}
Compile.prototype = {
init: function () {
if (this.el) {
this.fragment = this.nodeToFragment(this.el);
this.compileElement(this.fragment);
this.el.appendChild(this.fragment);
}
},
// 虛擬dom (使用循環將真實dom添加到
// document.createDocumentFragment創建的虛擬dom中)
nodeToFragment: function (el) {
// https://developer.mozilla.org/zh-CN/docs/Web/API/Document/createDocumentFragment
var fragment = document.createDocumentFragment(),
child;
while (child = el.firstChild) {
fragment.appendChild(child);
}
return fragment;
},
// 標簽屬性方法 http://www.runoob.com/jsref/dom-obj-all.html
// 處理標簽
compileElement: function (el) {
var that = this;
var childNodes = el.childNodes;
Array.prototype.slice.call(childNodes).forEach(node => {
var textContent = node.textContent;
// console.log(textContent)
var reg = /\{\{\s*(\w+)\s*\}\}/
// 判斷node節點類型
if (that.isElementNode(node)) {
that.compile(node);
} else if (this.isTextNode(node) && reg.exec(textContent)) {
// console.log(reg.exec(textContent));
this.compileText(node, reg.exec(textContent)[1]);
}
if (node.childNodes && node.childNodes.length) {
this.compileElement(node);
}
});
},
// 處理文本內容
compileText: function (node, exp) {
var that = this;
var val = this.vm.data[exp];
that.updateText(node, val);
new Watcher(this.vm, exp, function (val) {
that.updateText(node, val);
});
},
compile: function (node) {
var that = this;
var attributes = node.attributes;
Array.prototype.slice.call(attributes).forEach(attribute => {
var attrName = attribute.name,
attrValue = attribute.value;
if (that.isDirective(attrName)) {
var dir = attrName.substring(2);
if (that.isEventDirective(dir)) {
// 事件
that.compileEvent(node, that.vm, attrValue, dir);
} else {
// v-model
that.compileModel(node, that.vm, attrValue);
}
node.removeAttribute(attrName);
}
});
},
compileEvent: function(node, vm, exp, dir) {
var method = dir.split(":")[1];
var cb = vm.methods && vm.methods[exp];
if (method && cb) {
node.addEventListener(method, cb.bind(vm));
}
},
compileModel: function (node, vm, exp) {
var that = this;
var val = vm.data[exp];
that.updateModel(node, val);
new Watcher(vm, exp, function (value) {
that.updateModel(node, value);
});
// http://www.runoob.com/jsref/met-element-addeventlistener.html
// 觸發input事件,修改變量
node.addEventListener("input", function (e) {
var newVal = e.target.value;
if (newVal !== val) {
vm.data[exp] = newVal;
}
});
},
updateText: function (node, value) {
node.textContent = typeof value === 'undefined' ? '' : value;
},
updateModel: function (node, value) {
node.value = typeof value === 'undefined' ? '' : value;
},
isDirective: function (attr) {
return attr.indexOf('v-') === 0;
},
isEventDirective: function (dir) {
return dir.indexOf('on:') === 0;
},
// 元素節點
isElementNode: function (node) {
return node.nodeType == 1;
},
isTextNode: function (node) {
return node.nodeType == 3;
}
}
```
- 筆記內容來源
- 你不知道的JavaScript上
- vue
- 環境搭建
- node和npm安裝配置
- 安裝vue-cli并初始化vue項目
- 安裝配置elementUI
- vuex安裝配置
- axios安裝配置
- main.js
- vue基礎入門
- vue-router介紹
- vuex
- vue 原理學習源碼學習
- js正則處理v-bind和語法
- 雙向綁定
- 虛擬dom
- mvvm和render函數
- vue工作項目筆記
- elementUI 表格分頁多選記憶功能
- elementUI表格展開一行
- keepAlive
- vue整合ckeditor5
- this.$router.push 內打開新窗口
- java修改上傳圖片的權限
- 兼容ie11
- 生成二維碼
- base64圖片下載(兼容IE10)
- vue新手引導程序intro.js
- vue插件 devtools
- vue刷新當前頁面
- vue 錨點導航
- axios
- axios與springmvc
- vue-cli 3搭建vue
- git
- git常用命令
- 正則表達式
- 實例demo
- 1
- 新手引導頁
- 純css3從左顯示下劃線動畫導航菜單
- 純css3從中間顯示下劃線動畫導航菜單
- css顯示密碼
- 倒計時時鐘
- 星星評分
- 按鈕懸停效果
- 步驟條
- css動畫按鈕
- input標題獲得焦點上移
- css圖片放大
- css鏡像導航欄
- js
- 通信
- for in 和 for of
- 前端安全問題
- Promise
- 掘金冴羽學習筆記
- 模擬call
- 模擬bind
- 閉包
- 1 作用域
- 2 執行上下文棧
- 3 變量對象
- 4 作用域鏈
- 5 this
- 面向對象
- 基礎知識點
- 渲染機制
- 其他
- 判斷是否為數組
- http
- css
- 基礎知識
- css陰影