>[success] # 简单的响应式模型(二)
* 有这么一个 Vue 对象。修改了`data`中`text3`的数据,但是因为视图中并不需要用到`text3`,因此其实`text3`并不需要做响应劫持,上个章节时候我们是同一将所有数据都做了劫持,这是需要优化点之一
~~~
new Vue({
template:
`<div>
<span>{{text1}}</span>
<span>{{text2}}</span>
<div>`,
data: {
text1: 'text1',
text2: 'text2',
text3: 'text3'
}
});
// 修改数据
this.text3 = 'modify text3';
~~~
* 也会遇到一个数据对应多个视图情况
~~~
let globalObj = {
text1: 'text1'
};
let o1 = new Vue({
template:
`<div>
<span>{{text1}}</span>
<div>`,
data: globalObj
});
let o2 = new Vue({
template:
`<div>
<span>{{text1}}</span>
<div>`,
data: globalObj
});
// 执行
globalObj.text1 = 'hello,text1';
~~~
>[danger] ##### 简单的构想
![](https://img.kancloud.cn/eb/b5/ebb5142a0119611bfc362adb0da03738_908x486.png)
1. 创建Vue 对象时候 开始调用 `observer `给每个对象属性分配`getter `和` setter`,此时只是会先注册需要具有响应依赖的 发布者对象 ,并给每个属性 分配并不会触发getter 和setter中的观察者模,getter 和setter 只有触发时候才会调用现在只是声明
2. 在创建 我们要做的观察者对象`new Watcher()`,这里很巧妙 因为我们是先注册 `observer` 后注册 观察者对象,你此时并不能将观察者对象倒序传入`observer`。但在`Watcher `时候我们其实创建一个属于发布者的静态属性,该属性指向了我们订阅者
3. 此时开始渲染视图,只有视图中使用的属性才会进行 发布订阅的劫持,此时是第一次触发`getter `,将观察者注入到发布者,当我们进行`setter `赋值的时候 会触发发布者 统一广播调用观察者的视图更新
* 注实际上一个 `Watcher` 对象可能会在多个 `Dep` 中,并不是每次 **addSub 都是一个新的 Watcher 对象**,需依赖 `Dep.target` 进行收集(**实际上 Dep.target 也是通过 Watcher 对象的 get 方法调用 pushTarget 将自身赋值给 Dep.target**)。
~~~
// 发布者
class Dep {
constructor() {
this.subs = []
}
// 收集
addSubs(sub) {
this.subs.push(sub)
}
// 触发
notify() {
this.subs.forEach((sub) => {
sub.update()
})
}
}
// 观察者 关于Dep.target = this // this 指向的就是 new watcher 生成的watcher实例
// 这样全局任何地方都可调用
class Watcher {
constructor() {
Dep.target = this // this 指向的就是 new watcher 生成的watcher实例
}
/* 更新视图的方法 */
update() {
console.log('视图更新啦~')
}
}
class Vue {
constructor(opt) {
this._data = opt.data
observer(this._data)
/* 新建一个Watcher观察者对象,这时候Dep.target会指向这个Watcher对象 */
new Watcher()
/* 在这里模拟render的过程,为了触发test属性的get函数 */
console.log('render~', this._data.test)
}
}
function defineReactive(obj, key, val) {
/* 一个Dep类对象 */
const dep = new Dep()
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
/* 将Dep.target(即当前的Watcher对象存入dep的subs中) */
dep.addSub(Dep.target)
return val
},
set: function reactiveSetter(newVal) {
if (newVal === val) return
/* 在set的时候触发dep的notify来通知所有的Watcher对象更新视图 */
dep.notify()
},
})
}
~~~
>[danger] ##### 参考
[剖析 Vue.js 内部运行机制](https://juejin.cn/book/6844733705089449991)
- 工程化 -- Node
- vscode -- 插件
- vscode -- 代码片段
- 前端学会调试
- 谷歌浏览器调试技巧
- 权限验证
- 包管理工具 -- npm
- 常见的 npm ci 指令
- npm -- npm install安装包
- npm -- package.json
- npm -- 查看包版本信息
- npm - package-lock.json
- npm -- node_modules 层级
- npm -- 依赖包规则
- npm -- install 安装流程
- npx
- npm -- 发布自己的包
- 包管理工具 -- pnpm
- 模拟数据 -- Mock
- 页面渲染
- 渲染分析
- core.js && babel
- core.js -- 到底是什么
- 编译器那些术语
- 词法解析 -- tokenize
- 语法解析 -- ast
- 遍历节点 -- traverser
- 转换阶段、生成阶段略
- babel
- babel -- 初步上手之了解
- babel -- 初步上手之各种配置(preset-env)
- babel -- 初步上手之各种配置@babel/helpers
- babel -- 初步上手之各种配置@babel/runtime
- babel -- 初步上手之各种配置@babel/plugin-transform-runtime
- babel -- 初步上手之各种配置(babel-polyfills )(未来)
- babel -- 初步上手之各种配置 polyfill-service
- babel -- 初步上手之各种配置(@babel/polyfill )(过去式)
- babel -- 总结
- 各种工具
- 前端 -- 工程化
- 了解 -- Yeoman
- 使用 -- Yeoman
- 了解 -- Plop
- node cli -- 开发自己的脚手架工具
- 自动化构建工具
- Gulp
- 模块化打包工具为什么出现
- 模块化打包工具(新) -- webpack
- 简单使用 -- webpack
- 了解配置 -- webpack.config.js
- webpack -- loader 浅解
- loader -- 配置css模块解析
- loader -- 图片和字体(4.x)
- loader -- 图片和字体(5.x)
- loader -- 图片优化loader
- loader -- 配置解析js/ts
- webpack -- plugins 浅解
- eslit
- plugins -- CleanWebpackPlugin(4.x)
- plugins -- CleanWebpackPlugin(5.x)
- plugin -- HtmlWebpackPlugin
- plugin -- DefinePlugin 注入全局成员
- webapck -- 模块解析配置
- webpack -- 文件指纹了解
- webpack -- 开发环境运行构建
- webpack -- 项目环境划分
- 模块化打包工具 -- webpack
- webpack -- 打包文件是个啥
- webpack -- 基础配置项用法
- webpack4.x系列学习
- webpack -- 常见loader加载器
- webpack -- 移动端px转rem处理
- 开发一个自己loader
- webpack -- plugin插件
- webpack -- 文件指纹
- webpack -- 压缩css和html构建
- webpack -- 清里构建包
- webpack -- 复制静态文件
- webpack -- 自定义插件
- wepack -- 关于静态资源内联
- webpack -- source map 对照包
- webpack -- 环境划分构建
- webpack -- 项目构建控制台输出
- webpack -- 项目分析
- webpack -- 编译提速优护体积
- 提速 -- 编译阶段
- webpack -- 项目优化
- webpack -- DefinePlugin 注入全局成员
- webpack -- 代码分割
- webpack -- 页面资源提取
- webpack -- import按需引入
- webpack -- 摇树
- webpack -- 多页面打包
- webpack -- eslint
- webpack -- srr打包后续看
- webpack -- 构建一个自己的配置后续看
- webpack -- 打包组件和基础库
- webpack -- 源码
- webpack -- 启动都做了什么
- webpack -- cli做了什么
- webpack - 5
- 模块化打包工具 -- Rollup
- 工程化搭建代码规范
- 规范化标准--Eslint
- eslint -- 扩展配置
- eslint -- 指令
- eslint -- vscode
- eslint -- 原理
- Prettier -- 格式化代码工具
- EditorConfig -- 编辑器编码风格
- 检查提交代码是否符合检查配置
- 整体流程总结
- 微前端
- single-spa
- 简单上手 -- single-spa
- 快速理解systemjs
- single-sap 不使用systemjs
- monorepo -- 工程
- Vue -- 响应式了解
- Vue2.x -- 源码分析
- 发布订阅和观察者模式
- 简单 -- 了解响应式模型(一)
- 简单 -- 了解响应式模型(二)
- 简单 --了解虚拟DOM(一)
- 简单 --了解虚拟DOM(二)
- 简单 --了解diff算法
- 简单 --了解nextick
- Snabbdom -- 理解虚拟dom和diff算法
- Snabbdom -- h函数
- Snabbdom - Vnode 函数
- Snabbdom -- init 函数
- Snabbdom -- patch 函数
- 手写 -- 虚拟dom渲染
- Vue -- minVue
- vue3.x -- 源码分析
- 分析 -- reactivity
- 好文
- grpc -- 浏览器使用gRPC
- grcp-web -- 案例
- 待续