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会解决,但后者有兼容问题,还是主要讲原理。

  1. function defineProperty(obj, key, val){
  2. Object.defineProperty(obj, key, {
  3. get(){
  4. return val // 产生了闭包
  5. },
  6. set(newVal){
  7. if(newVal !== val){val=newVal} // 这里要修改val,涉及到闭包
  8. }
  9. })
  10. }

在这个基本的函数上进一步添加DOM修改办法。这里有问题就是不支持嵌套,还是要进一步封装成递归。这就是 Observer

  1. // 设定响应式
  2. function defineReactive(obj){
  3. }
  4. // 给一个对象,就把第一层做成响应式
  5. function observe(obj) {
  6. if(typeof obj !== 'object' || object === null) return ; // 排除对象和null
  7. Object.keys(obj).forEach(key => definedReactive(obj, key, obj[key])) // 遍历同一层object进行响应式处理
  8. }
  9. observe({x:1,a:{b:{c:1}}})

路由

监听url变化,然后匹配规则。

  • hash
  • history

hash

网址#参数发生变化,监听 hashchange 事件。
兼容性好

histroy

通过 history.pushStatehistory.replaceState
也就是直接操作历史记录。
监听的是popstate事件

哪种好?看情况。

vue配置路由:

  • 引入 vue vueRouter
  • 引入 pages
  • vue使用router插件
  • 定义路由
  • 导出

生命周期

  • beforeCreate 拿不到 props 和 data
  • created 可以访问数据了
  • beforeMount开始创建 vdom
  • monted渲染
  • beforeUpdateupdate

keep-alive独有的生命周期

  • activated
  • deactivated
    切换不销毁,会缓存,然后执行后者。恢复缓存时候执行前者
  • beforeDestroy
  • destoryed

组件通信

父子组件通信

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 作为参数传入。

源码好简单

  1. /* @flow */
  2. import { toArray } from '../util/index'
  3. export function initUse (Vue: GlobalAPI) {
  4. Vue.use = function (plugin: Function | Object) {
  5. // 是否已安装,避免重复注册
  6. const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
  7. if (installedPlugins.indexOf(plugin) > -1) {
  8. return this
  9. }
  10. // Vue.use(xxx)
  11. // 获取plugins之后的其他参数
  12. const args = toArray(arguments, 1)
  13. // 把当前this vue实例丢进去放第一个
  14. args.unshift(this)
  15. // 这样,第一个参数是Vue
  16. // 这就是为什么一提 插件,就想到 install,这里会自动执行
  17. if (typeof plugin.install === 'function') {
  18. plugin.install.apply(plugin, args)
  19. } else if (typeof plugin === 'function') {
  20. plugin.apply(null, args)
  21. }
  22. // 安装了就push到已安装列表
  23. installedPlugins.push(plugin)
  24. return this
  25. }
  26. }

细节不用看,只看注释就好了。围绕第一个参数有了一通操作,保证第一个参数是真正的Vue

参考资料

  • 官方文档 Vue.use
  • 《深入浅出Vue.js》13.4 全局api的实现原理
  • Vue.js 技术揭秘 Vue.use 部分
  • 开课吧 杨老师教程