重点js基础/Vue框架/typescript/Css/node/工具/网络基础
- Mvvm原理的解读以及模拟实现
- Vuex源码以及模拟实现(a week)
- Vue计算属性(原理及相关特性)
- express完整应用(two week)
前期还是需要在好好学学vue相关的源码知识,特别是mvvm以及vuex的源码阅读,大概领会其思想,目的很简单,就是面试的时候,不能被面试官问倒。
后面会尽量往node/webpack/ast靠拢,如果可能有时间的话,算法还是要了解。
源码调试不方便,不知道是自己的问题还是没有找到规律,vuex的源码是放在vuex.esm.js
中,在2.0版本的时候,源码中还含有src
文件夹,里面含有未压缩的各模块代码,现在vuex不是这个样子
Mvvm的模拟实现
1 2 3 4 5 6 7
| Observer 数据监听器,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者,内部采用Object.defineProperty的getter和setter来实现。
Compile 指令解析器,它的作用对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数。
Watcher 订阅者, 作为连接 Observer 和 Compile 的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数。
Dep 消息订阅器,内部维护了一个数组,用来收集订阅者(Watcher),数据变动触发notify 函数,再调用订阅者的 update 方法。
|
Vuex源码以及模拟实现
源码这么多,不知道如何下手?不知道主线是什么,谁能告诉我如何把源码理清,其实,有好几篇文章里的思路还是比较明晰的,无奈自己太次,有点理解不了尤大大的用意,老是被一些api打断思路。
还有就是对数据结构没啥概念,比如说在deom里定义了两个模块,
1 2 3 4 5 6
| { modules: { Test, Fuck, } }
|
这个modules
所包含的对象即使传入Store中的对象,即源码中的options
手写基础版的vuex
vuex对外暴露一个是install方法,传入vue,并把$cppStore绑定到组件上,到时候直接调用this.$cppStore.state
即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| let Vue: any function install(initVue: any) { Vue = initVue Vue.mixin({ beforeCreate() { const options = this.$options console.log('this', options); if (options && options.cppStore) { this.$cppStore = typeof options.cppStore === 'function' ? options.cppStore() : options.cppStore; } else { this.$cppStore = options.parent && options.parent.$cppStore; } } }) }
|
第二个是对外暴露一个 CppStore
class,这个class需要传入一个对象,一般是这种样子的对象
1 2 3 4 5 6
| { state: {}, actions: {}, mutations: {}, getters: {} }
|
- 安装引入以及使用
1 2 3 4 5 6 7
| import cppStore from './store/cppStore'; new Vue({ router, store, cppStore: (cppStore as any), render: (h) => h(App), }).$mount('#app');
|
store/cppStore1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import vue from 'vue' import CppVuex from './../../utils/vuex' vue.use(CppVuex) export default new CppVuex.CppStore({ state: { count: 10000, data: { age: 20, name: '' } }, actions: { addCount(context: any, val: number) { const obj = { age: val, name: 'cpp' } context.commit('CHANGE_COUNT', obj) } }, mutations: { CHANGE_COUNT(state: any, val: any) { state.count += val.age state.data = val } }, getters: { GetCount: (state: any) => { return state.count } } })
|
Vuex 乞丐版如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| let Vue: any function install(initVue: any) { Vue = initVue Vue.mixin({ beforeCreate() { const options = this.$options console.log('this', options); if (options && options.cppStore) { this.$cppStore = typeof options.cppStore === 'function' ? options.cppStore() : options.cppStore; } else { this.$cppStore = options.parent && options.parent.$cppStore; } } }) } class CppStore { protected $options: any = {} protected state: any = {} protected mutations: any = {} protected actions: any = {} protected getters: any = {} constructor(options: any = {}) { this.$options = options this.state = new Vue({ data: this.$options.state }) this.$options.getters && this.handleGetters(this.$options.getters) this.actions = this.$options.actions this.mutations = this.$options.mutations const store = this const { dispatch, commit } = this } protected handleGetters(getters: any) { this.getters = {} Object.keys(getters).forEach((key: any) => { Object.defineProperty(this.getters, key, { get() { return getters[key](this.state) } }) }) } protected dispatch(type: any, arg: any) { this.actions[type]({ commit: this.commit, state: this.state, getters: this.getters, dispatch: this.dispatch }, arg) } protected commit = (type: any, arg: any) => { this.mutations[type](this.state, arg) } } export default { CppStore, install }
|
intstall vuex安装
安装部分,核心就是给Vue注入一个store属性
1 2 3 4 5 6 7 8 9 10 11 12
| /** * Vuex init hook, injected into each instances init hooks list. */ function vuexInit () { const options = this.$options // store injection if (options.store) { this.$store = options.store } else if (options.parent && options.parent.$store) { this.$store = options.parent.$store } }
|
模拟action
vuex中通过dispatch触发action
- 知识点
如何判断函数是否是async函数1 2
| console.log('是否是async函数', this.action[type].constructor); console.log('是否是async函数', this.action[type].constructor.name === 'AsyncFunction');
|
参考连接
手写简单的mvvm
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <body> <div id="app"> <h2>{{message}}</h2> <input v-model="c" type="text"> </div> <script src="./MVVM.js"></script> <script> let mvvm = new Mvvm({ el: '#app', data: { a: { b: 1 }, c: 2, message: 'this is Message' } }); console.log('mvvm', mvvm); </script>
|
具体的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
| function Mvvm(options = {}) { this.$options = options let data = this._data = this.$options.data observe(data)
for (let key in data) { Object.defineProperty(this, key, { configurable: true, get() { return this._data[key] }, set(newValue) { this._data[key] = newValue; } }) }
new Compile(options.el, this) }
function Observe(data = {}) { let dep = new Dep() for (let key in data) { let val = data[key]; observe(val); Object.defineProperty(data, key, { configurable: true, get() { Dep.target && dep.addSub(Dep.target) return val }, set(newVal) { if (val === newVal) return; val = newVal; observe(newVal) } }) } }
function observe(val) { if (!val || typeof val !== 'object') { return -1 } return new Observe(val) }
function Compile(el, vm) { vm.$el = document.querySelector(el); let fragment = document.createDocumentFragment(); let child while(child = vm.$el.firstChild) { fragment.appendChild(child) console.log('child', child); console.log('fragment', fragment); } function replace(frag) { Array.from(frag.childNodes).forEach(node => { let txt = node.textContent; let reg = /\{\{(.*?)\}\}/g; console.log('node', node); if (node.nodeType === 3 && reg.test(txt)) { function replaceTxt() { node.textContent = txt.replace(reg, (matched, placeholder) => { console.log(placeholder); new Watcher(vm, placeholder, replaceTxt); return placeholder.split('.').reduce((val, key) => { return val[key]; }, vm); }); }; replaceTxt(); } if (node.nodeType === 1) { let nodeAttr = node.attributes; Array.from(nodeAttr).forEach(attr => { let name = attr.name; let exp = attr.value; if (name.includes('v-')){ node.value = vm[exp]; } new Watcher(vm, exp, function(newVal) { node.value = newVal; }); node.addEventListener('input', e => { let newVal = e.target.value; vm[exp] = newVal; }); }); } if (node.childNodes && node.childNodes.length) { replace(node); } }) } replace(fragment); vm.$el.appendChild(fragment); }
function Dep() { this.subs = [] } Dep.prototype = { addSub(sub) { this.subs.push(sub) }, notify() { console.log('this.subs', this.subs); this.subs.forEach(sub => { sub.update() }) } }
function Watcher(vm, exp, fn) { if (!vm && !exp) return; this.vm = vm; this.exp = exp; this.fn = fn; Dep.target = this; let arr = exp.split('.'); let val = vm; arr.forEach(key => { val = val[key]; }); Dep.target = null; } Watcher.prototype.update = function() { let arr = this.exp.split('.'); let val = this.vm; arr.forEach(key => { val = val[key]; }); this.fn(val) }
|
yun….一头雾水,我太难了!!!