2019-11-26 初稿
按:
Vue 文档越读越有意思 2019-11-26 09:27:42
MVVM
MVC
传统的mvc,使用控制器更新模型,视图从模型中获取数据。用户生成数据通过控制器更新模型,通知视图更新。
m-model 本地数据,数据库数据
v-view 视图 模板
问题,c承担的责任太大
VM
一般的产品都是先通过接口从数据库读取数据,然后数据处理后展示到视图上。
用户产生数据,然后通过接口写入数据库。
VM - ViewModel ,这里的vm值关心数据和业务处理,不管view如何处理数据。
这样view和model 就独立了。把一些逻辑放到vm里,让其他view使用这个vm
vue里,vm就是组件实例,v是模板。model在引入vuex的情况下可以和组件分离。
mvvm,通过vm把视图状态和用户状态分离成一个抽象。
那你自己实现一个 MVVM?
这里应该放一个地址,还真有。
Virtual DOM
结论: Virtual DOM 最大的意义不在于降低操作成本,而是在于通过对象来表述DOM结构,这样更容易来跨端。
细节可以看我的其他一篇文章,请搜索 Virtual DOM
diff算法
diff常见,对比文件变化。这里是为了找到js对象的区别。
两个核心函数:
- patch(container, vnode) 从js编程dom节点,递归实现dom节点渲染。
- patch(vnode, newVnode) 第一步算法需要判断tagName是否相同,如果不同就是被换了。如果没换就判断是否有子元素。
创建和更新。
diff的过程不能完整遍历,这样太麻烦。考虑到很少跨越层次移动DOM,只需要对比同层的节点,而不是跨层对比。
- 从上到下,从左到右递归遍历对象,给节点添加索引,方便以后对比差异
- 一旦节点有子元素,就判断元素是否不同
比如一个数据发生了变化,这就用到key了。
https://github.com/livoras/blog/issues/13
https://juejin.im/post/59bfbd736fb9a00a52065ec7
总结
virtual DOM 优势
- 提供性能。当然了静态变化还是 InnerHTML 更好
- 作为兼容层,可以跨端,反正都是对象的映射
- 高度抽象,从此之后不再需要操作dom了
Vue 三大技术核心
- 响应式。如何监听到data发生变化,数据劫持 Object.defineProperty Array 和 Proxy
- 模板。模板被解析,字符串差值、指令 v-bind model for if
- 渲染。如何渲染成页面, 字符串模板 —> Vdom —> DOM
响应式原理
在js中如何侦测一个对象的变化?
- Objecct.defineProperty 和 Array
- Proxy
vue用的是前者,有些缺陷如果改为proxy会解决,但后者有兼容问题,还是主要讲原理。
function defineProperty(obj, key, val){Object.defineProperty(obj, key, {get(){return val // 产生了闭包},set(newVal){if(newVal !== val){val=newVal} // 这里要修改val,涉及到闭包}})}
在这个基本的函数上进一步添加DOM修改办法。这里有问题就是不支持嵌套,还是要进一步封装成递归。这就是 Observer
// 设定响应式function defineReactive(obj){}// 给一个对象,就把第一层做成响应式function observe(obj) {if(typeof obj !== 'object' || object === null) return ; // 排除对象和nullObject.keys(obj).forEach(key => definedReactive(obj, key, obj[key])) // 遍历同一层object进行响应式处理}observe({x:1,a:{b:{c:1}}})
路由
监听url变化,然后匹配规则。
hashhistory
hash
网址#参数发生变化,监听 hashchange 事件。
兼容性好
histroy
通过 history.pushState 和 history.replaceState
也就是直接操作历史记录。
监听的是popstate事件
哪种好?看情况。
vue配置路由:
- 引入 vue vueRouter
- 引入 pages
- vue使用router插件
- 定义路由
- 导出
生命周期
beforeCreate拿不到 props 和 datacreated可以访问数据了beforeMount开始创建 vdommonted渲染beforeUpdate和update
keep-alive独有的生命周期
activateddeactivated
切换不销毁,会缓存,然后执行后者。恢复缓存时候执行前者beforeDestroydestoryed
组件通信
父子组件通信
props-emit
v-model 会解析成 prop和 input,语法糖。.sync是语法糖,简单实现子向父组件通信。
兄弟组件通信
通过查找父组件 this.$parent.$children通过name查询
跨多层次组件通信
provide/inject一般不推荐使用
- A组件,
provide对象数据 - B组件,
inject能收到传来的数据
父组件A,跨多层次的子组件B
任意组件
vuex 和 event-bus 了
computed watch
计算属性,依赖其他属性计算值,有缓存
监听,可以执行回调
为什么模板里data是函数
如果是对象,那就随便改了,如果是函数,每次都是新的。
响应式原理
这个书里面很详细了,可以单独开一篇Object.defineProperty
有 了对象的操作,还差数组,所以有了第二章的内容
NxtTick 原理分析
vue2.4之前,都是 microtask
但 microtask 优先级高,可能会比事件冒泡还快。如果使用macrotask 会太慢,渲染问题。
所以,默认 microtask ,特殊情况下macrotask 比如 v-on
推荐了一本书
https://ustbhuangyi.github.io/vue-analysis/
vuex
还是要看文档vuex
使用过程:
- 引入 vue vuex
- vue使用插件 vuex
- 定义 store
- 定义 state
- mutations
- actions
- getters
初始化main.js 里,记得导入 store
根组件new Vue注册store
$attrs
自己展开。inheritAttrs: false
Vue.use
在实际项目中,我们引入 router vuex 都是通过 Vue.use(router) 来使用的。那么 Vue.use的作用、原理、实现方式是什么,这里做个小总结。
安装 Vue.js 插件。如果插件是一个对象,必须提供
install方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入。
/* @flow */import { toArray } from '../util/index'export function initUse (Vue: GlobalAPI) {Vue.use = function (plugin: Function | Object) {// 是否已安装,避免重复注册const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))if (installedPlugins.indexOf(plugin) > -1) {return this}// Vue.use(xxx)// 获取plugins之后的其他参数const args = toArray(arguments, 1)// 把当前this vue实例丢进去放第一个args.unshift(this)// 这样,第一个参数是Vue// 这就是为什么一提 插件,就想到 install,这里会自动执行if (typeof plugin.install === 'function') {plugin.install.apply(plugin, args)} else if (typeof plugin === 'function') {plugin.apply(null, args)}// 安装了就push到已安装列表installedPlugins.push(plugin)return this}}
细节不用看,只看注释就好了。围绕第一个参数有了一通操作,保证第一个参数是真正的Vue
参考资料
- 官方文档 Vue.use
- 《深入浅出Vue.js》13.4 全局api的实现原理
- Vue.js 技术揭秘 Vue.use 部分
- 开课吧 杨老师教程
