因为 vue 主要是通过数据劫持来实现的, 通过 get,set 来完成数据的读取和更新
这里简单实现 data,v-model,computed
1 模版
- <!DOCTYPE html>
- <HTML>
- <head>
- <title>
- </title>
- </head>
- <body>
- <div id="app">
- <div>
- {{a.a}}
- </div>
- <div>
- {{c}}
- </div>
- <input type="text" v-model="c">
- {{hello}}
- </div>
- <script src="./mvvm.js">
- </script>
- <script type="text/javascript">
- let vue = new Vue2({
- el: "#app",
- data: {
- a: {
- a: 1
- },
- b: [1, 2, 3],
- c: 123123
- },
- computed: {
- hello() {
- return this.a.a + this.c;
- }
- }
- })
- </script>
- </body>
- </HTML>
- JS
- function Vue2(options = {}) {
- this.$options = options;
- let data = this._data = this.$options.data;
- observe(data);
- // this 代理了 this._data
- for (let key in data) {
- Object.defineProperty(this, key, {
- enumerable: true,
- get() {
- return this._data[key];
- },
- set(newVal) {
- this._data[key] = newVal
- }
- })
- }
- initComputed.call(this);// 初始化 computed
- new Compile(options.el, this)
- }
- // 计算属性的实现
- function initComputed(){
- let vm = this;
- let computed = this.$options.computed;
- Object.keys(computed).forEach(function (k) {
- Object.defineProperty(vm,k,{
- // 如果给的是函数那么调取函数, 如果是对象那么获取对象的 get 方法
- get:typeof computed[k] === 'function'?computed[k]:computed[k].get(),
- set(){}
- })
- })
- }
- function Compile(el, vm) {
- // el 表示替换的范围
- vm.$el = document.querySelector(el);
- let frament = document.createDocumentFragment();// 文案碎片
- while (child = vm.$el.firstChild) {// 将 App 中的内容, 移入到内存中
- frament.appendChild(child)
- }
- // 查找节点中的 {{}}
- replace(frament);
- function replace(frament) {
- Array.from(frament.childNodes).forEach(function (node) {
- let text = node.textContent;
- let reg = /\{\{(.*)\}\}/;
- // 判断 {{}}
- if (node.nodeType === 3 && reg.test(text)) {
- console.log(RegExp.$1)// 取出花括号中的变量
- let arr = RegExp.$1.split('.');//[a,a],[b],[c]
- let val = vm;
- arr.forEach(function (k) {
- val = val[k]
- })
- new Watcher(vm,RegExp.$1,function (newVal) {
- node.textContent = text.replace(/\{\{(.*)\}\}/, newVal)
- })
- //
- node.textContent = text.replace(/\{\{(.*)\}\}/, val)
- }
- // 判断 v-model
- if(node.nodeType ===1 /* 元素节点 */ ){
- let nodeAttrs = node.attributes;// 获取属性
- console.log(nodeAttrs)
- Array.from(nodeAttrs).forEach(function (attr) {
- console.log(attr)
- let name = attr.name;
- let exp = attr.value;
- if(name.indexOf('v-')==0){
- node.value = vm[exp];
- }
- new Watcher(vm,exp,function (newVal) {
- node.value = newVal;
- });
- node.addEventListener('input',function (e) {
- let newVal = e.target.value;
- vm[exp] = newVal;
- })
- })
- }
- if (node.childNodes) {
- replace(node)
- }
- });
- }
- vm.$el.appendChild(frament)
- }
- // 观察者 给队形增加 Object.defineProperty
- // 数据劫持
- function Observe(data) {
- let dep = new Dep();
- for (let key in data) {
- let val = data[key];
- observe(val);
- Object.defineProperty(data, key, {
- enumerable: true,
- get() {
- Dep.target&&dep.addSub(Dep.target);// 监控值的变化
- return val
- },
- set(newVal) {
- if (newVal === val) {
- return
- }
- val = newVal;
- observe(newVal)
- dep.notify()// 让所有的 watch 的 update 方法执行
- }
- })
- }
- }
- function observe(data) {
- if (typeof data !== "object") return;
- return Observe(data)
- }
- // 发布订阅
- function Dep() {
- this.subs = []
- }
- Dep.prototype.addSub = function (sub) {// 订阅
- this.subs.push(sub)
- }
- Dep.prototype.notify = function () {
- this.subs.forEach(sub=>sub.update())
- }
- function Watcher(vm,exp,fn) {
- this.vm = vm;
- this.exp = exp;
- this.fn = fn;
- Dep.target = this;
- let val = vm;
- let arr = exp.split('.')
- arr.forEach(function (k) {
- val = val[k]
- })
- Dep.target = null;
- }
- Watcher.prototype.update=function () {
- let val = this.vm;
- let arr = this.exp.split('.');
- arr.forEach(function (k) {
- val = val[k]
- })
- this.fn(val)
- }
来源: http://www.bubuko.com/infodetail-3043554.html