We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
小程序也是通过数据去驱动视图的渲染,需要手动的调用setData去完成这样一个动作。同时小程序的视图层也提供了用户交互的响应事件系统,在 js 代码中可以去注册相关的事件回调并在回调中去更改相关数据的值。Mpx 使用 Mobx 作为响应式数据工具并引入到小程序当中,使得小程序也有一套完成的响应式的系统,让小程序的开发有了更好的体验。
setData
还是从组件的角度开始分析 mpx 的整个响应式的系统。每次通过createComponent方法去创建一个新的组件,这个方法将原生的小程序创造组件的方法Component做了一层代理,例如在 attched 的生命周期钩子函数内部会注入一个 mixin:
createComponent
Component
// attached 生命周期钩子 mixin attached() { // 提供代理对象需要的api transformApiForProxy(this, currentInject) // 缓存options this.$rawOptions = rawOptions // 原始的,没有剔除 customKeys 的 options 配置 // 创建proxy对象 const mpxProxy = new MPXProxy(rawOptions, this) // 将当前实例代理到 MPXProxy 这个代理对象上面去 this.$mpxProxy = mpxProxy // 在小程序实例上绑定 $mpxProxy 的实例 // 组件监听视图数据更新, attached之后才能拿到properties this.$mpxProxy.created() }
在这个方法内部首先调用transformApiForProxy方法对组件实例上下文this做一层代理工作,在 context 上下文上去重置小程序的 setData 方法,同时拓展 context 相关的属性内容:
transformApiForProxy
this
function transformApiForProxy (context, currentInject) { const rawSetData = context.setData.bind(context) // setData 绑定对应的 context 上下文 Object.defineProperties(context, { setData: { // 重置 context 的 setData 方法 get () { return this.$mpxProxy.setData.bind(this.$mpxProxy) }, configurable: true }, __getInitialData: { get () { return () => context.data }, configurable: false }, __render: { // 小程序原生的 setData 方法 get () { return rawSetData }, configurable: false } }) // context 绑定注入的render函数 if (currentInject) { if (currentInject.render) { // 编译过程中生成的 render 函数 Object.defineProperties(context, { __injectedRender: { get () { return currentInject.render.bind(context) }, configurable: false } }) } if (currentInject.getRefsData) { Object.defineProperties(context, { __getRefsData: { get () { return currentInject.getRefsData }, configurable: false } }) } } }
接下来实例化一个 mpxProxy 实例并挂载至 context 上下文的 $mpxProxy 属性上,并调用 mpxProxy 的 created 方法完成这个代理对象的初始化的工作。在 created 方法内部主要是完成了以下的几个工作:
$watch
$forceUpdate
$updated
$nextTick
这里我们具体的来看下 initRender 方法内部是如何进行工作的:
export default class MPXProxy { ... initRender() { let renderWatcher let renderExcutedFailed = false if (this.target.__injectedRender) { // webpack 注入的有关这个 page/component 的 renderFunction renderWatcher = watch(this.target, () => { if (renderExcutedFailed) { this.render() } else { try { return this.target.__injectedRender() // 执行 renderFunction,获取渲染所需的响应式数据 } catch(e) { ... } } }, { handler: (ret) => { if (!renderExcutedFailed) { this.renderWithData(ret) // 渲染页面 } }, immediate: true, forceCallback: true }) } } ... }
在 initRender 方法内部非常清楚的看到,首先判断这个 page/component 是否具有 renderFunction,如果有的话那么就直接实例化一个 renderWatcher:
export default class Watcher { constructor (context, expr, callback, options) { this.destroyed = false this.get = () => { return type(expr) === 'String' ? getByPath(context, expr) : expr() } const callbackType = type(callback) if (callbackType === 'Object') { options = callback callback = null } else if (callbackType === 'String') { callback = context[callback] } this.callback = typeof callback === 'function' ? action(callback.bind(context)) : null this.options = options || {} this.id = ++uid // 创建一个新的 reaction this.reaction = new Reaction(`mpx-watcher-${this.id}`, () => { this.update() }) // 在调用 getValue 函数的时候,实际上是调用 reaction.track 方法,这个方法内部会自动执行 effect 函数,即执行 this.update() 方法,这样便会出发一次模板当中的 render 函数来完成依赖的收集 const value = this.getValue() if (this.options.immediateAsync) { // 放置到一个队列里面去执行 queueWatcher(this) } else { // 立即执行 callback this.value = value if (this.options.immediate) { this.callback && this.callback(this.value) } } } getValue () { let value this.reaction.track(() => { value = this.get() // 获取注入的 render 函数执行后返回的 renderData 的值,在执行 render 函数的过程中,就会访问响应式数据的值 if (this.options.deep) { const valueType = type(value) // 某些情况下,最外层是非isObservable 对象,比如同时观察多个属性时 if (!isObservable(value) && (valueType === 'Array' || valueType === 'Object')) { if (valueType === 'Array') { value = value.map(item => toJS(item, false)) } else { const newValue = {} Object.keys(value).forEach(key => { newValue[key] = toJS(value[key], false) }) value = newValue } } else { value = toJS(value, false) } } else if (isObservableArray(value)) { value.peek() } else if (isObservableObject(value)) { keys(value) } }) return value } update () { if (this.options.sync) { this.run() } else { queueWatcher(this) } } run () { const immediateAsync = !this.hasOwnProperty('value') const oldValue = this.value this.value = this.getValue() // 重新获取新的 renderData 的值 if (immediateAsync || this.value !== oldValue || isObject(this.value) || this.options.forceCallback) { if (this.callback) { immediateAsync ? this.callback(this.value) : this.callback(this.value, oldValue) } } } destroy () { this.destroyed = true this.reaction.getDisposer()() } }
Watcher 观察者核心实现的工作流程就是:
this.update()
mpx 在构建这个响应式的系统当中,主要有2个大的环节,其一为在构建编译的过程中,将 template 模块转化为 renderFunction,提供了渲染模板时所需响应式数据的访问机制,并将 renderFunction 注入到运行时代码当中,其二就是在运行环节,mpx 通过构建一个小程序实例的代理对象,将小程序实例上的数据访问全部代理至 MPXProxy 实例上,而 MPXProxy 实例即 mpx 基于 Mobx 去构建的一套响应式数据对象,首先将 data 数据转化为响应式数据,其次提供了 computed 计算属性,watch 方法等一系列增强的拓展属性/方法,虽然在你的业务代码当中 page/component 实例 this 都是小程序提供的,但是最终经过代理机制,实际上访问的是 MPXProxy 所提供的增强功能,所以 mpx 也是通过这样一个代理对象去接管了小程序的实例。需要特别指出的是,mpx 将小程序官方提供的 setData 方法同样收敛至内部,这也是响应式系统提供的基础能力,即开发者只需要关注业务开发,而有关小程序渲染运行在 mpx 内部去帮你完成。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
小程序也是通过数据去驱动视图的渲染,需要手动的调用
setData
去完成这样一个动作。同时小程序的视图层也提供了用户交互的响应事件系统,在 js 代码中可以去注册相关的事件回调并在回调中去更改相关数据的值。Mpx 使用 Mobx 作为响应式数据工具并引入到小程序当中,使得小程序也有一套完成的响应式的系统,让小程序的开发有了更好的体验。还是从组件的角度开始分析 mpx 的整个响应式的系统。每次通过
createComponent
方法去创建一个新的组件,这个方法将原生的小程序创造组件的方法Component
做了一层代理,例如在 attched 的生命周期钩子函数内部会注入一个 mixin:在这个方法内部首先调用
transformApiForProxy
方法对组件实例上下文this
做一层代理工作,在 context 上下文上去重置小程序的 setData 方法,同时拓展 context 相关的属性内容:接下来实例化一个 mpxProxy 实例并挂载至 context 上下文的 $mpxProxy 属性上,并调用 mpxProxy 的 created 方法完成这个代理对象的初始化的工作。在 created 方法内部主要是完成了以下的几个工作:
$watch
,$forceUpdate
,$updated
,$nextTick
等方法,这样在你的业务代码当中即可直接访问实例上部署好的这些方法;这里我们具体的来看下 initRender 方法内部是如何进行工作的:
在 initRender 方法内部非常清楚的看到,首先判断这个 page/component 是否具有 renderFunction,如果有的话那么就直接实例化一个 renderWatcher:
Watcher 观察者核心实现的工作流程就是:
this.update()
方法来完成页面的重新渲染。mpx 在构建这个响应式的系统当中,主要有2个大的环节,其一为在构建编译的过程中,将 template 模块转化为 renderFunction,提供了渲染模板时所需响应式数据的访问机制,并将 renderFunction 注入到运行时代码当中,其二就是在运行环节,mpx 通过构建一个小程序实例的代理对象,将小程序实例上的数据访问全部代理至 MPXProxy 实例上,而 MPXProxy 实例即 mpx 基于 Mobx 去构建的一套响应式数据对象,首先将 data 数据转化为响应式数据,其次提供了 computed 计算属性,watch 方法等一系列增强的拓展属性/方法,虽然在你的业务代码当中 page/component 实例 this 都是小程序提供的,但是最终经过代理机制,实际上访问的是 MPXProxy 所提供的增强功能,所以 mpx 也是通过这样一个代理对象去接管了小程序的实例。需要特别指出的是,mpx 将小程序官方提供的 setData 方法同样收敛至内部,这也是响应式系统提供的基础能力,即开发者只需要关注业务开发,而有关小程序渲染运行在 mpx 内部去帮你完成。
The text was updated successfully, but these errors were encountered: