提交 32f99d02 编写于 作者: fxy060608's avatar fxy060608

feat(v3): mp runtime

上级 a0fe152b
...@@ -1918,7 +1918,10 @@ function flushCallbacks () { ...@@ -1918,7 +1918,10 @@ function flushCallbacks () {
// where microtasks have too high a priority and fire in between supposedly // where microtasks have too high a priority and fire in between supposedly
// sequential events (e.g. #4521, #6690, which have workarounds) // sequential events (e.g. #4521, #6690, which have workarounds)
// or even between bubbling of the same event (#6566). // or even between bubbling of the same event (#6566).
var timerFunc; // fixed by xxxxxx app-plus 平台 Promise 执行顺序不一致,导致各种乱七八糟的 Bug,统一使用 setTimeout
var timerFunc = function () {
setTimeout(flushCallbacks, 0);
};
// The nextTick behavior leverages the microtask queue, which can be accessed // The nextTick behavior leverages the microtask queue, which can be accessed
// via either native Promise.then or MutationObserver. // via either native Promise.then or MutationObserver.
...@@ -1927,48 +1930,50 @@ var timerFunc; ...@@ -1927,48 +1930,50 @@ var timerFunc;
// completely stops working after triggering a few times... so, if native // completely stops working after triggering a few times... so, if native
// Promise is available, we will use it: // Promise is available, we will use it:
/* istanbul ignore next, $flow-disable-line */ /* istanbul ignore next, $flow-disable-line */
if (typeof Promise !== 'undefined' && isNative(Promise) && !isIOS) { // fixed by xxxxxx // if (typeof Promise !== 'undefined' && isNative(Promise)) {
var p = Promise.resolve(); // const p = Promise.resolve()
timerFunc = function () { // timerFunc = () => {
p.then(flushCallbacks); // p.then(flushCallbacks)
// In problematic UIWebViews, Promise.then doesn't completely break, but // // In problematic UIWebViews, Promise.then doesn't completely break, but
// it can get stuck in a weird state where callbacks are pushed into the // // it can get stuck in a weird state where callbacks are pushed into the
// microtask queue but the queue isn't being flushed, until the browser // // microtask queue but the queue isn't being flushed, until the browser
// needs to do some other work, e.g. handle a timer. Therefore we can // // needs to do some other work, e.g. handle a timer. Therefore we can
// "force" the microtask queue to be flushed by adding an empty timer. // // "force" the microtask queue to be flushed by adding an empty timer.
if (isIOS) { setTimeout(noop); } // if (isIOS) setTimeout(noop)
}; // }
} else if (!isIE && typeof MutationObserver !== 'undefined' && ( // isUsingMicroTask = true
isNative(MutationObserver) || // } else if (!isIE && typeof MutationObserver !== 'undefined' && (
// PhantomJS and iOS 7.x // isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]' // // PhantomJS and iOS 7.x
)) { // MutationObserver.toString() === '[object MutationObserverConstructor]'
// Use MutationObserver where native Promise is not available, // )) {
// e.g. PhantomJS, iOS7, Android 4.4 // // Use MutationObserver where native Promise is not available,
// (#6466 MutationObserver is unreliable in IE11) // // e.g. PhantomJS, iOS7, Android 4.4
var counter = 1; // // (#6466 MutationObserver is unreliable in IE11)
var observer = new MutationObserver(flushCallbacks); // let counter = 1
var textNode = document.createTextNode(String(counter)); // const observer = new MutationObserver(flushCallbacks)
observer.observe(textNode, { // const textNode = document.createTextNode(String(counter))
characterData: true // observer.observe(textNode, {
}); // characterData: true
timerFunc = function () { // })
counter = (counter + 1) % 2; // timerFunc = () => {
textNode.data = String(counter); // counter = (counter + 1) % 2
}; // textNode.data = String(counter)
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { // }
// Fallback to setImmediate. // isUsingMicroTask = true
// Technically it leverages the (macro) task queue, // } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
// but it is still a better choice than setTimeout. // // Fallback to setImmediate.
timerFunc = function () { // // Technically it leverages the (macro) task queue,
setImmediate(flushCallbacks); // // but it is still a better choice than setTimeout.
}; // timerFunc = () => {
} else { // setImmediate(flushCallbacks)
// Fallback to setTimeout. // }
timerFunc = function () { // } else {
setTimeout(flushCallbacks, 0); // // Fallback to setTimeout.
}; // timerFunc = () => {
} // setTimeout(flushCallbacks, 0)
// }
// }
function nextTick (cb, ctx) { function nextTick (cb, ctx) {
var _resolve; var _resolve;
...@@ -2258,18 +2263,29 @@ function mergeVNodeHook (def, hookKey, hook) { ...@@ -2258,18 +2263,29 @@ function mergeVNodeHook (def, hookKey, hook) {
/* */ /* */
// fixed by xxxxxx (mp properties) // fixed by xxxxxx (mp properties)
function extractPropertiesFromVNodeData(data, Ctor, res) { function extractPropertiesFromVNodeData(data, Ctor, res, context) {
var propOptions = Ctor.options.mpOptions && Ctor.options.mpOptions.properties; var propOptions = Ctor.options.mpOptions && Ctor.options.mpOptions.properties;
if (isUndef(propOptions)) { if (isUndef(propOptions)) {
return res return res
} }
var externalClasses = Ctor.options.mpOptions.externalClasses || [];
var attrs = data.attrs; var attrs = data.attrs;
var props = data.props; var props = data.props;
if (isDef(attrs) || isDef(props)) { if (isDef(attrs) || isDef(props)) {
for (var key in propOptions) { for (var key in propOptions) {
var altKey = hyphenate(key); var altKey = hyphenate(key);
checkProp(res, props, key, altKey, true) || var result = checkProp(res, props, key, altKey, true) ||
checkProp(res, attrs, key, altKey, false); checkProp(res, attrs, key, altKey, false);
// externalClass
if (
result &&
res[key] &&
externalClasses.indexOf(altKey) !== -1 &&
context[camelize(res[key])]
) {
// 赋值 externalClass 真正的值(模板里 externalClass 的值可能是字符串)
res[key] = context[camelize(res[key])];
}
} }
} }
return res return res
...@@ -2278,7 +2294,8 @@ function extractPropertiesFromVNodeData(data, Ctor, res) { ...@@ -2278,7 +2294,8 @@ function extractPropertiesFromVNodeData(data, Ctor, res) {
function extractPropsFromVNodeData ( function extractPropsFromVNodeData (
data, data,
Ctor, Ctor,
tag tag,
context
) { ) {
// we are only extracting raw values here. // we are only extracting raw values here.
// validation and default values are handled in the child // validation and default values are handled in the child
...@@ -2286,7 +2303,7 @@ function extractPropsFromVNodeData ( ...@@ -2286,7 +2303,7 @@ function extractPropsFromVNodeData (
var propOptions = Ctor.options.props; var propOptions = Ctor.options.props;
if (isUndef(propOptions)) { if (isUndef(propOptions)) {
// fixed by xxxxxx // fixed by xxxxxx
return extractPropertiesFromVNodeData(data, Ctor, {}) return extractPropertiesFromVNodeData(data, Ctor, {}, context)
} }
var res = {}; var res = {};
var attrs = data.attrs; var attrs = data.attrs;
...@@ -2315,7 +2332,7 @@ function extractPropsFromVNodeData ( ...@@ -2315,7 +2332,7 @@ function extractPropsFromVNodeData (
} }
} }
// fixed by xxxxxx // fixed by xxxxxx
return extractPropertiesFromVNodeData(data, Ctor, res) return extractPropertiesFromVNodeData(data, Ctor, res, context)
} }
function checkProp ( function checkProp (
...@@ -3158,6 +3175,8 @@ var componentVNodeHooks = { ...@@ -3158,6 +3175,8 @@ var componentVNodeHooks = {
// fixed by xxxxxx // fixed by xxxxxx
componentInstance._isMounted = true; componentInstance._isMounted = true;
if (componentInstance._$vd) {// 延迟 mounted if (componentInstance._$vd) {// 延迟 mounted
callHook(componentInstance, 'onServiceCreated');
callHook(componentInstance, 'onServiceAttached');
componentInstance._$vd.addMountedVm(componentInstance); componentInstance._$vd.addMountedVm(componentInstance);
} else { } else {
callHook(componentInstance, 'mounted'); callHook(componentInstance, 'mounted');
...@@ -3249,7 +3268,7 @@ function createComponent ( ...@@ -3249,7 +3268,7 @@ function createComponent (
} }
// extract props // extract props
var propsData = extractPropsFromVNodeData(data, Ctor, tag); var propsData = extractPropsFromVNodeData(data, Ctor, tag, context);
// functional component // functional component
if (isTrue(Ctor.options.functional)) { if (isTrue(Ctor.options.functional)) {
...@@ -4110,6 +4129,8 @@ function mountComponent ( ...@@ -4110,6 +4129,8 @@ function mountComponent (
// fixed by xxxxxx // fixed by xxxxxx
vm._isMounted = true; vm._isMounted = true;
if (vm._$vd) {// 延迟 mounted 事件 if (vm._$vd) {// 延迟 mounted 事件
callHook(vm, 'onServiceCreated');
callHook(vm, 'onServiceAttached');
vm._$vd.addMountedVm(vm); vm._$vd.addMountedVm(vm);
} else { } else {
callHook(vm, 'mounted'); callHook(vm, 'mounted');
......
...@@ -26,7 +26,7 @@ function hasOwn (obj, key) { ...@@ -26,7 +26,7 @@ function hasOwn (obj, key) {
return hasOwnProperty.call(obj, key) return hasOwnProperty.call(obj, key)
} }
function noop () {} function noop () { }
/** /**
* Create a cached version of a pure function. * Create a cached version of a pure function.
...@@ -50,8 +50,8 @@ const camelize = cached((str) => { ...@@ -50,8 +50,8 @@ const camelize = cached((str) => {
const SOURCE_KEY = '__data__'; const SOURCE_KEY = '__data__';
const COMPONENT_LIFECYCLE = { const COMPONENT_LIFECYCLE = {
'created': 'created', 'created': 'onServiceCreated',
'attached': 'created', 'attached': 'onServiceAttached',
'ready': 'mounted', 'ready': 'mounted',
'moved': 'moved', 'moved': 'moved',
'detached': 'destroyed' 'detached': 'destroyed'
...@@ -382,6 +382,102 @@ function parseComponent (mpComponentOptions) { ...@@ -382,6 +382,102 @@ function parseComponent (mpComponentOptions) {
return vueComponentOptions return vueComponentOptions
} }
function initRelationHandlers (type, handler, target, ctx) {
if (!handler) {
return
}
const name = `_$${type}Handlers`;
(ctx[name] || (ctx[name] = [])).push(function () {
handler.call(ctx, target);
});
}
function initLinkedHandlers (relation, target, ctx) {
const type = 'linked';
const name = relation.name;
const relationNodes = ctx._$relationNodes || (ctx._$relationNodes = Object.create(null));
(relationNodes[name] || (relationNodes[name] = [])).push(target);
initRelationHandlers(type, relation[type], target, ctx);
}
function initUnlinkedHandlers (relation, target, ctx) {
const type = 'unlinked';
initRelationHandlers(type, relation[type], target, ctx);
}
function findParentRelation (parentVm, target, type) {
const relations = parentVm &&
parentVm.$options.mpOptions &&
parentVm.$options.mpOptions.relations;
if (!relations) {
return []
}
const name = Object.keys(relations).find(name => {
const relation = relations[name];
return relation.target === target && relation.type === type
});
if (!name) {
return []
}
return [relations[name], parentVm]
}
function initParentRelation (vm, childRelation, match) {
const [parentRelation, parentVm] = match(vm, vm.$options.mpOptions.path);
if (!parentRelation) {
return
}
initLinkedHandlers(parentRelation, vm, parentVm);
initLinkedHandlers(childRelation, parentVm, vm);
initUnlinkedHandlers(parentRelation, vm, parentVm);
initUnlinkedHandlers(childRelation, parentVm, vm);
}
function initRelation (relation, vm) {
const type = relation.type;
if (type === 'parent') {
initParentRelation(vm, relation, function matchParent (vm, target) {
return findParentRelation(vm.$parent, target, 'child')
});
} else if (type === 'ancestor') {
initParentRelation(vm, relation, function matchAncestor (vm, target) {
let $parent = vm.$parent;
while ($parent) {
const ret = findParentRelation($parent, target, 'descendant');
if (ret.length) {
return ret
}
$parent = $parent.$parent;
}
return []
});
}
}
function initRelations (vm) {
const {
relations
} = vm.$options.mpOptions || {};
if (!relations) {
return
}
Object.keys(relations).forEach(name => {
initRelation(relations[name], vm);
});
}
function handleRelations (vm, type) {
// TODO 需要移除 relationNodes
const handlers = vm[`_$${type}Handlers`];
if (!handlers) {
return
}
handlers.forEach(handler => handler());
}
const sharedPropertyDefinition = { const sharedPropertyDefinition = {
enumerable: true, enumerable: true,
configurable: true, configurable: true,
...@@ -467,7 +563,12 @@ function validateProp (key, propsOptions, propsData, vm) { ...@@ -467,7 +563,12 @@ function validateProp (key, propsOptions, propsData, vm) {
value = !!value; value = !!value;
} }
const observer = propOptions && propOptions.observer; const observer = propOptions && propOptions.observer;
observer && observe(observer, vm, value); if (observer) {
// 初始化时,异步触发 observer,否则 observer 中无法访问 methods 或其他
setTimeout(function () {
observe(observer, vm, value);
}, 4);
}
return value return value
} }
return getPropertyVal(propsOptions[key]) return getPropertyVal(propsOptions[key])
...@@ -589,102 +690,6 @@ function initMethods (vm) { ...@@ -589,102 +690,6 @@ function initMethods (vm) {
vm._$updateProperties = updateProperties; vm._$updateProperties = updateProperties;
} }
function initRelationHandlers (type, handler, target, ctx, handlerCtx) {
if (!handler) {
return
}
const name = `_$${type}Handlers`;
(handlerCtx[name] || (handlerCtx[name] = [])).push(function () {
handler.call(ctx, target);
});
}
function initLinkedHandlers (relation, target, ctx, handlerCtx) {
const type = 'linked';
const name = relation.name;
const relationNodes = ctx._$relationNodes || (ctx._$relationNodes = Object.create(null));
(relationNodes[name] || (relationNodes[name] = [])).push(target);
initRelationHandlers(type, relation[type], target, ctx, handlerCtx);
}
function initUnlinkedHandlers (relation, target, ctx, handlerCtx) {
const type = 'unlinked';
initRelationHandlers(type, relation[type], target, ctx, handlerCtx);
}
function findParentRelation (parentVm, target, type) {
const relations = parentVm &&
parentVm.$options.mpOptions &&
parentVm.$options.mpOptions.relations;
if (!relations) {
return []
}
const name = Object.keys(relations).find(name => {
const relation = relations[name];
return relation.target === target && relation.type === type
});
if (!name) {
return []
}
return [relations[name], parentVm]
}
function initParentRelation (vm, childRelation, match) {
const [parentRelation, parentVm] = match(vm, vm.$options.mpOptions.path);
if (!parentRelation) {
return
}
// 先父后子
initLinkedHandlers(parentRelation, vm, parentVm, vm);
initLinkedHandlers(childRelation, parentVm, vm, vm);
initUnlinkedHandlers(parentRelation, vm, parentVm, vm);
initUnlinkedHandlers(childRelation, parentVm, vm, vm);
}
function initRelation (relation, vm) {
const type = relation.type;
if (type === 'parent') {
initParentRelation(vm, relation, function matchParent (vm, target) {
return findParentRelation(vm.$parent, target, 'child')
});
} else if (type === 'ancestor') {
initParentRelation(vm, relation, function matchAncestor (vm, target) {
let $parent = vm.$parent;
while ($parent) {
const ret = findParentRelation($parent, target, 'descendant');
if (ret.length) {
return ret
}
$parent = $parent.$parent;
}
return []
});
}
}
function initRelations (vm) {
const {
relations
} = vm.$options.mpOptions || {};
if (!relations) {
return
}
Object.keys(relations).forEach(name => {
initRelation(relations[name], vm);
});
}
function handleRelations (vm, type) {
// TODO 需要移除 relationNodes
const handlers = vm[`_$${type}Handlers`];
if (!handlers) {
return
}
handlers.forEach(handler => handler());
}
function handleObservers (vm) { function handleObservers (vm) {
const watch = vm.$options.watch; const watch = vm.$options.watch;
if (!watch) { if (!watch) {
...@@ -705,13 +710,14 @@ function handleObservers (vm) { ...@@ -705,13 +710,14 @@ function handleObservers (vm) {
var polyfill = { var polyfill = {
beforeCreate () { beforeCreate () {
// 取消 development 时的 Proxy,避免小程序组件模板中使用尚未定义的属性告警
this._renderProxy = this;
},
created () { // properties 中可能会访问 methods,故需要在 created 中初始化
initState(this); initState(this);
initMethods(this); initMethods(this);
initRelations(this); initRelations(this);
}, },
created () {
handleRelations(this, 'linked');
},
mounted () { mounted () {
handleObservers(this); handleObservers(this);
}, },
...@@ -749,10 +755,21 @@ function Page (options) { ...@@ -749,10 +755,21 @@ function Page (options) {
global['__wxComponents'][global['__wxRoute']] = pageOptions; global['__wxComponents'][global['__wxRoute']] = pageOptions;
} }
function initRelationsHandler (vueComponentOptions) {
// linked 需要在当前组件 attached 之后再执行
if (!vueComponentOptions['onServiceAttached']) {
vueComponentOptions['onServiceAttached'] = [];
}
vueComponentOptions['onServiceAttached'].push(function onServiceAttached () {
handleRelations(this, 'linked');
});
}
function Component (options) { function Component (options) {
const componentOptions = parseComponent(options); const componentOptions = parseComponent(options);
componentOptions.mixins.unshift(polyfill); componentOptions.mixins.unshift(polyfill);
componentOptions.mpOptions.path = global['__wxRoute']; componentOptions.mpOptions.path = global['__wxRoute'];
initRelationsHandler(componentOptions);
global['__wxComponents'][global['__wxRoute']] = componentOptions; global['__wxComponents'][global['__wxRoute']] = componentOptions;
} }
......
...@@ -112,5 +112,12 @@ describe('codegen', () => { ...@@ -112,5 +112,12 @@ describe('codegen', () => {
`with(this){return _c('view',{staticClass:_$s(0,'sc',"bbbb"),attrs:{"id":"aaa","_i":0}})}` `with(this){return _c('view',{staticClass:_$s(0,'sc',"bbbb"),attrs:{"id":"aaa","_i":0}})}`
) )
}) })
// TODO 后续优化 dataset
// it('generate dataset', () => {
// assertCodegen(
// '<view data-a="1" :data-b="b"></view>',
// `with(this){return _c('view',{attrs:{"data-a":"1","data-b":b,"_i":0}})}`
// )
// })
}) })
/* eslint-enable quotes */ /* eslint-enable quotes */
...@@ -63,5 +63,12 @@ describe('codegen', () => { ...@@ -63,5 +63,12 @@ describe('codegen', () => {
`with(this){return _c('v-uni-view',{wxsProps:{"change:prop":"pos"},attrs:{"change:prop":swipe.sizeReady,"prop":_$gc(0,'change:pos'),"_i":0},on:{"touchstart":function($event){$event = $handleWxsEvent($event);swipe.touchstart($event, $getComponentDescriptor())},"touchmove":function($event){$event = $handleWxsEvent($event);swipe.touchmove($event, $getComponentDescriptor())},"touchend":function($event){$event = $handleWxsEvent($event);swipe.touchend($event, $getComponentDescriptor())},"change":function($event){return $handleViewEvent($event)}}})}` `with(this){return _c('v-uni-view',{wxsProps:{"change:prop":"pos"},attrs:{"change:prop":swipe.sizeReady,"prop":_$gc(0,'change:pos'),"_i":0},on:{"touchstart":function($event){$event = $handleWxsEvent($event);swipe.touchstart($event, $getComponentDescriptor())},"touchmove":function($event){$event = $handleWxsEvent($event);swipe.touchmove($event, $getComponentDescriptor())},"touchend":function($event){$event = $handleWxsEvent($event);swipe.touchend($event, $getComponentDescriptor())},"change":function($event){return $handleViewEvent($event)}}})}`
) )
}) })
// TODO 后续优化dataset
// it('generate dataset', () => {
// assertCodegen(
// '<view data-a="1" :data-b="b"></view>',
// `with(this){return _c('v-uni-view',{attrs:{"_i":0}})}`
// )
// })
}) })
/* eslint-enable quotes */ /* eslint-enable quotes */
const compiler = require('../lib') const compiler = require('../lib')
const res = compiler.compile( const res = compiler.compile(
` `
<view><van-grid-item v-for="(item,index) in (4)" :key="item"></van-grid-item></view> <view data-a="1" :data-b="b"></view>
`, { `, {
miniprogram: true, miniprogram: true,
resourcePath: '/User/fxy/Documents/test.wxml', resourcePath: '/User/fxy/Documents/test.wxml',
......
...@@ -63,7 +63,16 @@ function markStatic (node) { ...@@ -63,7 +63,16 @@ function markStatic (node) {
const isCustomComponent = isComponent(node.tag) const isCustomComponent = isComponent(node.tag)
if (node.attrs && !isCustomComponent && node.tag !== 'keep-alive') { // 移除静态属性 if (node.attrs && !isCustomComponent && node.tag !== 'keep-alive') { // 移除静态属性
// 保留 id 属性, selectComponent 需要使用 // 保留 id 属性, selectComponent 需要使用
node.attrs = node.attrs.filter(attr => attr.name === 'id' || attr.name === ID || isVar(attr.value)) node.attrs = node.attrs.filter(attr => {
const {
name,
value
} = attr
return name === 'id' ||
name === ID ||
// name.indexOf('data-') === 0 || // TODO dataset
isVar(value)
})
} }
node.children = node.children.filter(child => { // 移除静态文本 node.children = node.children.filter(child => { // 移除静态文本
......
...@@ -57,13 +57,18 @@ function parseDirs (el, genVar, ignoreDirs = []) { ...@@ -57,13 +57,18 @@ function parseDirs (el, genVar, ignoreDirs = []) {
function parseAttrs (el, genVar) { function parseAttrs (el, genVar) {
el.attrs && el.attrs.forEach(attr => { el.attrs && el.attrs.forEach(attr => {
const {
name,
value
} = attr
if ( if (
attr.name !== ID && name !== ID &&
attr.name.indexOf('change:') !== 0 && // wxs change:prop // name.indexOf('data-') !== 0 && // TODO dataset 保留
isVar(attr.value) && name.indexOf('change:') !== 0 && // wxs change:prop
attr.value.indexOf('_$') !== 0 // 已被提前处理过了,如 wxs prop:_$gc(2,'change:prop') isVar(value) &&
value.indexOf('_$') !== 0 // 已被提前处理过了,如 wxs prop:_$gc(2,'change:prop')
) { ) {
attr.value = genVar('a-' + attr.name, attr.value) attr.value = genVar('a-' + name, value)
} }
}) })
} }
......
...@@ -139,6 +139,11 @@ function transformNode (el, parent, state, isScopedSlot) { ...@@ -139,6 +139,11 @@ function transformNode (el, parent, state, isScopedSlot) {
parseWxsProps(el, { parseWxsProps(el, {
isAppView: true isAppView: true
}) })
// if (el.attrs) { // TODO 过滤 dataset
// el.attrs = el.attrs.filter(attr => attr.name.indexOf('data-') !== 0)
// }
parseAttrs(el, genVar) parseAttrs(el, genVar)
parseProps(el, genVar) parseProps(el, genVar)
......
export const SOURCE_KEY = '__data__' export const SOURCE_KEY = '__data__'
export const COMPONENT_LIFECYCLE = { export const COMPONENT_LIFECYCLE = {
'created': 'created', 'created': 'onServiceCreated',
'attached': 'created', 'attached': 'onServiceAttached',
'ready': 'mounted', 'ready': 'mounted',
'moved': 'moved', 'moved': 'moved',
'detached': 'destroyed' 'detached': 'destroyed'
......
...@@ -8,6 +8,10 @@ import { ...@@ -8,6 +8,10 @@ import {
parseComponent parseComponent
} from './parser/component-parser' } from './parser/component-parser'
import {
handleRelations
} from './polyfill/relations'
import polyfill from './polyfill/index' import polyfill from './polyfill/index'
export * from './wxs' export * from './wxs'
...@@ -23,10 +27,21 @@ export function Page (options) { ...@@ -23,10 +27,21 @@ export function Page (options) {
global['__wxComponents'][global['__wxRoute']] = pageOptions global['__wxComponents'][global['__wxRoute']] = pageOptions
} }
function initRelationsHandler (vueComponentOptions) {
// linked 需要在当前组件 attached 之后再执行
if (!vueComponentOptions['onServiceAttached']) {
vueComponentOptions['onServiceAttached'] = []
}
vueComponentOptions['onServiceAttached'].push(function onServiceAttached () {
handleRelations(this, 'linked')
})
}
export function Component (options) { export function Component (options) {
const componentOptions = parseComponent(options) const componentOptions = parseComponent(options)
componentOptions.mixins.unshift(polyfill) componentOptions.mixins.unshift(polyfill)
componentOptions.mpOptions.path = global['__wxRoute'] componentOptions.mpOptions.path = global['__wxRoute']
initRelationsHandler(componentOptions)
global['__wxComponents'][global['__wxRoute']] = componentOptions global['__wxComponents'][global['__wxRoute']] = componentOptions
} }
......
...@@ -17,13 +17,14 @@ import { ...@@ -17,13 +17,14 @@ import {
export default { export default {
beforeCreate () { beforeCreate () {
// 取消 development 时的 Proxy,避免小程序组件模板中使用尚未定义的属性告警
this._renderProxy = this
},
created () { // properties 中可能会访问 methods,故需要在 created 中初始化
initState(this) initState(this)
initMethods(this) initMethods(this)
initRelations(this) initRelations(this)
}, },
created () {
handleRelations(this, 'linked')
},
mounted () { mounted () {
handleObservers(this) handleObservers(this)
}, },
......
function initRelationHandlers (type, handler, target, ctx, handlerCtx) { function initRelationHandlers (type, handler, target, ctx) {
if (!handler) { if (!handler) {
return return
} }
const name = `_$${type}Handlers`; const name = `_$${type}Handlers`;
(handlerCtx[name] || (handlerCtx[name] = [])).push(function () { (ctx[name] || (ctx[name] = [])).push(function () {
handler.call(ctx, target) handler.call(ctx, target)
}) })
} }
function initLinkedHandlers (relation, target, ctx, handlerCtx) { function initLinkedHandlers (relation, target, ctx) {
const type = 'linked' const type = 'linked'
const name = relation.name const name = relation.name
const relationNodes = ctx._$relationNodes || (ctx._$relationNodes = Object.create(null)); const relationNodes = ctx._$relationNodes || (ctx._$relationNodes = Object.create(null));
(relationNodes[name] || (relationNodes[name] = [])).push(target) (relationNodes[name] || (relationNodes[name] = [])).push(target)
initRelationHandlers(type, relation[type], target, ctx, handlerCtx) initRelationHandlers(type, relation[type], target, ctx)
} }
function initUnlinkedHandlers (relation, target, ctx, handlerCtx) { function initUnlinkedHandlers (relation, target, ctx) {
const type = 'unlinked' const type = 'unlinked'
initRelationHandlers(type, relation[type], target, ctx, handlerCtx) initRelationHandlers(type, relation[type], target, ctx)
} }
function findParentRelation (parentVm, target, type) { function findParentRelation (parentVm, target, type) {
...@@ -44,12 +44,12 @@ function initParentRelation (vm, childRelation, match) { ...@@ -44,12 +44,12 @@ function initParentRelation (vm, childRelation, match) {
if (!parentRelation) { if (!parentRelation) {
return return
} }
// 先父后子
initLinkedHandlers(parentRelation, vm, parentVm, vm)
initLinkedHandlers(childRelation, parentVm, vm, vm)
initUnlinkedHandlers(parentRelation, vm, parentVm, vm) initLinkedHandlers(parentRelation, vm, parentVm)
initUnlinkedHandlers(childRelation, parentVm, vm, vm) initLinkedHandlers(childRelation, parentVm, vm)
initUnlinkedHandlers(parentRelation, vm, parentVm)
initUnlinkedHandlers(childRelation, parentVm, vm)
} }
function initRelation (relation, vm) { function initRelation (relation, vm) {
......
...@@ -43,7 +43,12 @@ function validateProp (key, propsOptions, propsData, vm) { ...@@ -43,7 +43,12 @@ function validateProp (key, propsOptions, propsData, vm) {
value = !!value value = !!value
} }
const observer = propOptions && propOptions.observer const observer = propOptions && propOptions.observer
observer && observe(observer, vm, value) if (observer) {
// 初始化时,异步触发 observer,否则 observer 中无法访问 methods 或其他
setTimeout(function () {
observe(observer, vm, value)
}, 4)
}
return value return value
} }
return getPropertyVal(propsOptions[key]) return getPropertyVal(propsOptions[key])
......
...@@ -28,7 +28,10 @@ const LIFECYCLE_HOOKS = [ ...@@ -28,7 +28,10 @@ const LIFECYCLE_HOOKS = [
// 'onReady', // 兼容旧版本,应该移除该事件 // 'onReady', // 兼容旧版本,应该移除该事件
'onPageShow', 'onPageShow',
'onPageHide', 'onPageHide',
'onPageResize' 'onPageResize',
// 小程序的 created,attached 生命周期(需要在 service 层的 Vue 内核 mounted 时触发,因小程序 created 可以使用 selectComponent)
'onServiceCreated',
'onServiceAttached'
] ]
export function lifecycleMixin (Vue) { export function lifecycleMixin (Vue) {
// fixed vue-class-component // fixed vue-class-component
......
...@@ -12,7 +12,15 @@ function parseSelector (selector) { ...@@ -12,7 +12,15 @@ function parseSelector (selector) {
if (selector.indexOf('#') === 0) { if (selector.indexOf('#') === 0) {
const id = selector.substr(1) const id = selector.substr(1)
return function match (vnode) { return function match (vnode) {
return vnode.data && vnode.data.attrs && vnode.data.attrs.id === id // props
if (vnode.componentInstance && vnode.componentInstance.id === id) {
return true
}
// attrs
if (vnode.data && vnode.data.attrs && vnode.data.attrs.id === id) {
return true
}
return false
} }
} else if (selector.indexOf('.') === 0) { } else if (selector.indexOf('.') === 0) {
const clazz = selector.substr(1) const clazz = selector.substr(1)
...@@ -52,17 +60,21 @@ function querySelectorAll (vm, matchSelector, ret) { ...@@ -52,17 +60,21 @@ function querySelectorAll (vm, matchSelector, ret) {
} }
const $children = vm.$children const $children = vm.$children
for (let i = 0; i < $children.length; i++) { for (let i = 0; i < $children.length; i++) {
const childVm = querySelectorAll($children[i], matchSelector, ret) querySelectorAll($children[i], matchSelector, ret)
childVm && ret.push(childVm)
} }
return ret
} }
export function initPolyfill (Vue) { export function initPolyfill (Vue) {
Vue.prototype.createIntersectionObserver = function createIntersectionObserver (options) {
return uni.createIntersectionObserver(this, options)
}
Vue.prototype.selectComponent = function selectComponent (selector) { Vue.prototype.selectComponent = function selectComponent (selector) {
return querySelector(this, parseSelector(selector)) return querySelector(this, parseSelector(selector))
} }
Vue.prototype.selectAllComponent = function selectAllComponent (selector) { Vue.prototype.selectAllComponents = function selectAllComponents (selector) {
return querySelectorAll(this, parseSelector(selector), []) return querySelectorAll(this, parseSelector(selector), [])
} }
} }
...@@ -13,6 +13,9 @@ function findVmById (id, vm) { ...@@ -13,6 +13,9 @@ function findVmById (id, vm) {
} }
export function findElm (component, pageVm) { export function findElm (component, pageVm) {
if (!pageVm) {
return console.error(`page is not ready`)
}
if (!component) { if (!component) {
return pageVm.$el return pageVm.$el
} }
......
...@@ -95,8 +95,8 @@ export function registerPage ({ ...@@ -95,8 +95,8 @@ export function registerPage ({
selectComponent (selector) { selectComponent (selector) {
return this.$vm.selectComponent(selector) return this.$vm.selectComponent(selector)
}, },
selectAllComponent (selector) { selectAllComponents (selector) {
return this.$vm.selectAllComponent(selector) return this.$vm.selectAllComponents(selector)
} }
} }
......
...@@ -144,6 +144,12 @@ function setData (id, name, value) { ...@@ -144,6 +144,12 @@ function setData (id, name, value) {
case V_FOR: case V_FOR:
return setForData.call(this, id, value) return setForData.call(this, id, value)
} }
// TODO 暂时先传递 dataset 至 view 层(理论上不需要)
if (name.indexOf('a-data-') === 0) {
try {
value = JSON.stringify(value)
} catch (e) {}
}
return ((this._$newData[id] || (this._$newData[id] = {}))[name] = value) return ((this._$newData[id] || (this._$newData[id] = {}))[name] = value)
} }
......
...@@ -3,7 +3,8 @@ import { ...@@ -3,7 +3,8 @@ import {
} from 'uni-shared' } from 'uni-shared'
import { import {
V_FOR V_FOR,
B_STYLE
} from '../../constants' } from '../../constants'
function setResult (data, k, v) { function setResult (data, k, v) {
...@@ -52,11 +53,14 @@ function diffElmData (newObj, oldObj) { ...@@ -52,11 +53,14 @@ function diffElmData (newObj, oldObj) {
old = oldObj[key] old = oldObj[key]
if (old !== cur) { if (old !== cur) {
// 全量同步 style (因为 style 可能会动态删除部分样式) // 全量同步 style (因为 style 可能会动态删除部分样式)
// if (key === B_STYLE && isPlainObject(cur) && isPlainObject(old)) { if (key === B_STYLE && isPlainObject(cur) && isPlainObject(old)) {
// const style = diffObject(cur, old) if (Object.keys(cur).length !== Object.keys(old).length) { // 长度不等
// style && setResult(result || (result = Object.create(null)), B_STYLE, style) setResult(result || (result = Object.create(null)), B_STYLE, cur)
// } else } else {
if (key === V_FOR && Array.isArray(cur) && Array.isArray(old)) { const style = diffObject(cur, old, false)
style && setResult(result || (result = Object.create(null)), B_STYLE, style)
}
} else if (key === V_FOR && Array.isArray(cur) && Array.isArray(old)) {
const vFor = diffArray(cur, old) const vFor = diffArray(cur, old)
vFor && setResult(result || (result = Object.create(null)), V_FOR, vFor) vFor && setResult(result || (result = Object.create(null)), V_FOR, vFor)
} else { } else {
......
...@@ -152,20 +152,20 @@ export class VDomSync { ...@@ -152,20 +152,20 @@ export class VDomSync {
this.initialized = true this.initialized = true
this.batchData.push([PAGE_CREATED, [this.pageId, this.pagePath]]) this.batchData.push([PAGE_CREATED, [this.pageId, this.pagePath]])
} }
this.batchData = this.batchData.filter(data => { const batchData = this.batchData.filter(data => {
if (data[0] === UPDATED_DATA && !Object.keys(data[1][1]).length) { if (data[0] === UPDATED_DATA && !Object.keys(data[1][1]).length) {
return false return false
} }
return true return true
}) })
if (this.batchData.length) { this.batchData.length = 0
if (batchData.length) {
UniServiceJSBridge.publishHandler(VD_SYNC, { UniServiceJSBridge.publishHandler(VD_SYNC, {
data: this.batchData, data: batchData,
options: { options: {
timestamp: Date.now() timestamp: Date.now()
} }
}, [this.pageId]) }, [this.pageId])
this.batchData.length = 0
} }
} }
......
...@@ -103,6 +103,9 @@ function vdSync ({ ...@@ -103,6 +103,9 @@ function vdSync ({
function getData (id, name) { function getData (id, name) {
try { try {
if (name.indexOf('a-data-') === 0) { // TODO 临时方案序列化,反序列化dataset,后续应该将dataset保留在service层
return JSON.parse(this.$r[id][name])
}
return this.$r[id][name] return this.$r[id][name]
} catch (e) { } catch (e) {
console.error(this.$options.__file + `:[${this._$id}]$r[${id}][${name}] is undefined`) console.error(this.$options.__file + `:[${this._$id}]$r[${id}][${name}] is undefined`)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册