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 ; // 排除对象和null
Object.keys(obj).forEach(key => definedReactive(obj, key, obj[key])) // 遍历同一层object进行响应式处理
}
observe({x:1,a:{b:{c:1}}})
路由
监听url变化,然后匹配规则。
hash
history
hash
网址#
参数发生变化,监听 hashchange
事件。
兼容性好
histroy
通过 history.pushState
和 history.replaceState
也就是直接操作历史记录。
监听的是popstate
事件
哪种好?看情况。
vue配置路由:
- 引入 vue vueRouter
- 引入 pages
- vue使用router插件
- 定义路由
- 导出
生命周期
beforeCreate
拿不到 props 和 datacreated
可以访问数据了beforeMount
开始创建 vdommonted
渲染beforeUpdate
和update
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 作为参数传入。
/* @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 部分
- 开课吧 杨老师教程