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

feat(runtime): uni-mp

上级 a9e781aa
...@@ -40,7 +40,8 @@ module.exports = { ...@@ -40,7 +40,8 @@ module.exports = {
}), }),
replace({ replace({
__GLOBAL__: platform.prefix, __GLOBAL__: platform.prefix,
__PLATFORM_TITLE__: platform.title __PLATFORM_TITLE__: platform.title,
__PLATFORM__: JSON.stringify(process.env.UNI_PLATFORM)
}) })
], ],
external: ['vue'] external: ['vue']
......
import Vue from 'vue';
const _toString = Object.prototype.toString; const _toString = Object.prototype.toString;
const hasOwnProperty = Object.prototype.hasOwnProperty; const hasOwnProperty = Object.prototype.hasOwnProperty;
...@@ -15,7 +17,9 @@ function isPlainObject (obj) { ...@@ -15,7 +17,9 @@ function isPlainObject (obj) {
function hasOwn (obj, key) { function hasOwn (obj, key) {
return hasOwnProperty.call(obj, key) return hasOwnProperty.call(obj, key)
} }
function noop () {}
const SYNC_API_RE = /hideKeyboard|upx2px|canIUse|^create|Sync$|Manager$/; const SYNC_API_RE = /hideKeyboard|upx2px|canIUse|^create|Sync$|Manager$/;
...@@ -209,6 +213,9 @@ const protocols = { ...@@ -209,6 +213,9 @@ const protocols = {
}, },
navigateBackMiniProgram: { navigateBackMiniProgram: {
name: 'navigateBackSmartProgram' name: 'navigateBackSmartProgram'
},
showShareMenu: {
name: 'openShare'
} }
}; };
...@@ -374,6 +381,418 @@ var api = /*#__PURE__*/Object.freeze({ ...@@ -374,6 +381,418 @@ var api = /*#__PURE__*/Object.freeze({
requestPayment: requestPayment requestPayment: requestPayment
}); });
const MOCKS = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__'];
function initMocks (vm) {
const mpInstance = vm.$mp[vm.mpType];
MOCKS.forEach(mock => {
if (hasOwn(mpInstance, mock)) {
vm[mock] = mpInstance[mock];
}
});
}
function initHooks (mpOptions, hooks, delay = false) {
hooks.forEach(hook => {
mpOptions[hook] = function (args) {
if (delay) {
setTimeout(() => this.$vm.__call_hook(hook, args));
} else {
this.$vm.__call_hook(hook, args);
}
};
});
}
function getData (vueOptions) {
let data = vueOptions.data || {};
const methods = vueOptions.methods || {};
if (typeof data === 'function') {
try {
data = data();
} catch (e) {
console.warn('根据 Vue 的 data 函数初始化小程序 data 失败,请尽量确保 data 函数中不访问 vm 对象,否则可能影响首次数据渲染速度。', data);
}
}
return Object.assign(data, methods)
}
const PROP_TYPES = [String, Number, Boolean, Object, Array, null];
function createObserver (name) {
return function observer (newVal, oldVal) {
if (this.$vm) {
this.$vm[name] = newVal; // 为了触发其他非 render watcher
}
}
}
function getProperties (props) {
const properties = {};
if (Array.isArray(props)) { // ['title']
props.forEach(key => {
properties[key] = {
type: null,
observer: createObserver(key)
};
});
} else if (isPlainObject(props)) { // {title:{type:String,default:''},content:String}
Object.keys(props).forEach(key => {
const opts = props[key];
if (isPlainObject(opts)) { // title:{type:String,default:''}
let value = opts['default'];
if (isFn(value)) {
value = value();
}
properties[key] = {
type: PROP_TYPES.includes(opts.type) ? opts.type : null,
value,
observer: createObserver(key)
};
} else { // content:String
properties[key] = {
type: PROP_TYPES.includes(opts) ? opts : null,
observer: createObserver(key)
};
}
});
}
return properties
}
function wrapper$1 (event) {
event.stopPropagation = noop;
event.preventDefault = noop;
event.target = event.target || {};
event.detail = event.detail || {};
{ // mp-baidu,checked=>value
if (hasOwn(event.detail, 'checked') && !hasOwn(event.detail, 'value')) {
event.detail.value = event.detail.checked;
}
}
// TODO 又得兼容 mpvue 的 mp 对象
event.mp = event;
event.target = Object.assign({}, event.target, event.detail);
return event
}
function processEventArgs (event, args = [], isCustom) {
if (isCustom && !args.length) { // 无参数,直接传入 detail 数组
return event.detail
}
const ret = [];
args.forEach(arg => {
if (arg === '$event') {
ret.push(isCustom ? event.detail[0] : event);
} else {
ret.push(arg);
}
});
return ret
}
const ONCE = '~';
const CUSTOM = '^';
function handleEvent (event) {
event = wrapper$1(event);
// [['tap',[['handle',[1,2,a]],['handle1',[1,2,a]]]]]
const eventOpts = (event.currentTarget || event.target).dataset.eventOpts;
if (!eventOpts) {
return console.warn(`事件信息不存在`)
}
// [['handle',[1,2,a]],['handle1',[1,2,a]]]
const eventType = event.type;
eventOpts.forEach(eventOpt => {
let type = eventOpt[0];
const eventsArray = eventOpt[1];
const isCustom = type.charAt(0) === CUSTOM;
type = isCustom ? type.slice(1) : type;
const isOnce = type.charAt(0) === ONCE;
type = isOnce ? type.slice(1) : type;
if (eventsArray && eventType === type) {
eventsArray.forEach(eventArray => {
const handler = this.$vm[eventArray[0]];
if (!isFn(handler)) {
throw new Error(` _vm.${eventArray[0]} is not a function`)
}
if (isOnce) {
if (handler.once) {
return
}
handler.once = true;
}
handler.apply(this.$vm, processEventArgs(event, eventArray[1], isCustom));
});
}
});
}
function handleLink (event) {
event.detail.$parent = this.$vm;
}
function initRefs (vm) {
const mpInstance = vm.$mp[vm.mpType];
Object.defineProperty(vm, '$refs', {
get () {
const $refs = Object.create(null);
const components = mpInstance.selectAllComponents('.vue-ref');
components.forEach(component => {
const ref = component.dataset.ref;
$refs[ref] = component.$vm;
});
const forComponents = mpInstance.selectAllComponents('.vue-ref-in-for');
forComponents.forEach(component => {
const ref = component.dataset.ref;
if (!$refs[ref]) {
$refs[ref] = [];
}
$refs[ref].push(component.$vm);
});
return $refs
}
});
}
function initChildren (vm) {
const mpInstance = vm.$mp[vm.mpType];
Object.defineProperty(vm, '$children', {
get () {
const $children = [];
const components = mpInstance.selectAllComponents('.vue-com');
components.forEach(component => {
$children.push(component.$vm);
});
return $children
}
});
}
function baiduComponentDestroy ($vm) {
$vm.$children.forEach(childVm => {
childVm.$mp.component.detached();
});
$vm.$mp.component.detached();
}
function baiduPageDestroy ($vm) {
$vm.$destroy();
$vm.$children.forEach(childVm => {
baiduComponentDestroy(childVm);
});
}
const hooks = [
'onShow',
'onHide',
'onError',
'onPageNotFound'
];
function createApp (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
// 外部初始化时 Vue 还未初始化,放到 createApp 内部初始化 mixin
Vue.mixin({
beforeCreate () {
if (!this.$options.mpType) {
return
}
this.mpType = this.$options.mpType;
this.$mp = {
data: {},
[this.mpType]: this.$options.mpInstance
};
delete this.$options.mpType;
delete this.$options.mpInstance;
if (this.mpType !== 'app') {
initRefs(this);
initMocks(this);
initChildren(this);
}
}
});
const appOptions = {
onLaunch (args) {
this.$vm = new Vue(Object.assign(vueOptions, {
mpType: 'app',
mpInstance: this
}));
this.$vm.$mount();
setTimeout(() => this.$vm.__call_hook('onLaunch', args));
}
};
initHooks(appOptions, hooks, true); // 延迟执行,因为 App 的注册在 main.js 之前,可能导致生命周期内 Vue 原型上开发者注册的属性无法访问
App(appOptions);
return vueOptions
}
const hooks$1 = [
'onShow',
'onHide',
'onPullDownRefresh',
'onReachBottom',
'onShareAppMessage',
'onPageScroll',
'onResize',
'onTabItemTap',
'onBackPress',
'onNavigationBarButtonTap',
'onNavigationBarSearchInputChanged',
'onNavigationBarSearchInputConfirmed',
'onNavigationBarSearchInputClicked'
];
function createPage (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
const pageOptions = {
data: getData(vueOptions),
onLoad (args) {
{
this.$baiduComponentInstances = Object.create(null);
}
this.$vm = new Vue(Object.assign(vueOptions, {
mpType: 'page',
mpInstance: this
}));
this.$vm.$mount();
this.$vm.__call_hook('onLoad', args);
},
onReady () {
this.$vm._isMounted = true;
this.$vm.__call_hook('onReady');
},
onUnload () {
this.$vm.__call_hook('onUnload');
{ // 百度组件不会在页面 unload 时触发 detached
baiduPageDestroy(this.$vm);
}
},
__e: handleEvent,
__l: handleLink
};
initHooks(pageOptions, hooks$1);
return Page(pageOptions)
}
function initVueComponent (mpInstace, VueComponent) {
if (mpInstace.$vm) {
return
}
const options = {
mpType: 'component',
mpInstance: mpInstace,
propsData: mpInstace.properties
};
// 初始化 vue 实例
mpInstace.$vm = new VueComponent(options);
// 初始化渲染数据
mpInstace.$vm.$mount();
}
function createComponent (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
const properties = getProperties(vueOptions.props);
const VueComponent = Vue.extend(vueOptions);
const componentOptions = {
options: {
multipleSlots: true,
addGlobalClass: true
},
data: getData(vueOptions),
properties,
lifetimes: {
attached () {
initVueComponent(this, VueComponent);
},
ready () {
initVueComponent(this, VueComponent); // 目前发现部分情况小程序 attached 不触发
{
const baiduComponentInstances = this.pageinstance.$baiduComponentInstances;
baiduComponentInstances[this.id] = this;
if (this.ownerId) { // 组件嵌组件
const parentBaiduComponentInstance = baiduComponentInstances[this.ownerId];
if (parentBaiduComponentInstance) {
this.$vm.$parent = parentBaiduComponentInstance.$vm;
} else {
console.error(`查找父组件失败${this.ownerId}`);
}
} else { // 页面直属组件
this.$vm.$parent = this.pageinstance.$vm;
}
}
const eventId = this.dataset.eventId;
if (eventId) {
const listeners = this.$vm.$parent.$mp.listeners;
if (listeners) {
const listenerOpts = listeners[eventId];
Object.keys(listenerOpts).forEach(eventType => {
listenerOpts[eventType].forEach(handler => {
this.$vm[handler.once ? '$once' : '$on'](eventType, handler);
});
});
}
}
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady');
},
detached () {
{
delete this.pageinstance.$baiduComponentInstances[this.id];
}
this.$vm.$destroy();
}
},
pageLifetimes: {
show (args) {
this.$vm.__call_hook('onPageShow', args);
},
hide () {
this.$vm.__call_hook('onPageHide');
},
resize (size) {
this.$vm.__call_hook('onPageResize', size);
}
},
methods: {
__e: handleEvent,
__l: handleLink
}
};
return Component(componentOptions)
}
let uni = {}; let uni = {};
if (typeof Proxy !== 'undefined') { if (typeof Proxy !== 'undefined') {
...@@ -422,3 +841,4 @@ if (typeof Proxy !== 'undefined') { ...@@ -422,3 +841,4 @@ if (typeof Proxy !== 'undefined') {
var uni$1 = uni; var uni$1 = uni;
export default uni$1; export default uni$1;
export { createApp, createPage, createComponent };
{ {
"name": "@dcloudio/uni-mp-baidu", "name": "@dcloudio/uni-mp-baidu",
"version": "0.0.7", "version": "0.0.804",
"description": "uni-app mp-baidu", "description": "uni-app mp-baidu",
"main": "dist/index.js", "main": "dist/index.js",
"scripts": { "scripts": {
......
import Vue from 'vue';
const _toString = Object.prototype.toString; const _toString = Object.prototype.toString;
const hasOwnProperty = Object.prototype.hasOwnProperty; const hasOwnProperty = Object.prototype.hasOwnProperty;
...@@ -15,7 +17,9 @@ function isPlainObject (obj) { ...@@ -15,7 +17,9 @@ function isPlainObject (obj) {
function hasOwn (obj, key) { function hasOwn (obj, key) {
return hasOwnProperty.call(obj, key) return hasOwnProperty.call(obj, key)
} }
function noop () {}
const SYNC_API_RE = /hideKeyboard|upx2px|canIUse|^create|Sync$|Manager$/; const SYNC_API_RE = /hideKeyboard|upx2px|canIUse|^create|Sync$|Manager$/;
...@@ -422,6 +426,380 @@ var api = /*#__PURE__*/Object.freeze({ ...@@ -422,6 +426,380 @@ var api = /*#__PURE__*/Object.freeze({
}); });
const MOCKS = ['__route__', '__wxExparserNodeId__', '__wxWebviewId__'];
function initMocks (vm) {
const mpInstance = vm.$mp[vm.mpType];
MOCKS.forEach(mock => {
if (hasOwn(mpInstance, mock)) {
vm[mock] = mpInstance[mock];
}
});
}
function initHooks (mpOptions, hooks, delay = false) {
hooks.forEach(hook => {
mpOptions[hook] = function (args) {
if (delay) {
setTimeout(() => this.$vm.__call_hook(hook, args));
} else {
this.$vm.__call_hook(hook, args);
}
};
});
}
function getData (vueOptions) {
let data = vueOptions.data || {};
const methods = vueOptions.methods || {};
if (typeof data === 'function') {
try {
data = data();
} catch (e) {
console.warn('根据 Vue 的 data 函数初始化小程序 data 失败,请尽量确保 data 函数中不访问 vm 对象,否则可能影响首次数据渲染速度。', data);
}
}
return Object.assign(data, methods)
}
const PROP_TYPES = [String, Number, Boolean, Object, Array, null];
function createObserver (name) {
return function observer (newVal, oldVal) {
if (this.$vm) {
this.$vm[name] = newVal; // 为了触发其他非 render watcher
}
}
}
function getProperties (props) {
const properties = {};
if (Array.isArray(props)) { // ['title']
props.forEach(key => {
properties[key] = {
type: null,
observer: createObserver(key)
};
});
} else if (isPlainObject(props)) { // {title:{type:String,default:''},content:String}
Object.keys(props).forEach(key => {
const opts = props[key];
if (isPlainObject(opts)) { // title:{type:String,default:''}
let value = opts['default'];
if (isFn(value)) {
value = value();
}
properties[key] = {
type: PROP_TYPES.includes(opts.type) ? opts.type : null,
value,
observer: createObserver(key)
};
} else { // content:String
properties[key] = {
type: PROP_TYPES.includes(opts) ? opts : null,
observer: createObserver(key)
};
}
});
}
return properties
}
function wrapper$1 (event) {
event.stopPropagation = noop;
event.preventDefault = noop;
event.target = event.target || {};
event.detail = event.detail || {};
// TODO 又得兼容 mpvue 的 mp 对象
event.mp = event;
event.target = Object.assign({}, event.target, event.detail);
return event
}
function processEventArgs (event, args = [], isCustom) {
if (isCustom && !args.length) { // 无参数,直接传入 detail 数组
return event.detail
}
const ret = [];
args.forEach(arg => {
if (arg === '$event') {
ret.push(isCustom ? event.detail[0] : event);
} else {
ret.push(arg);
}
});
return ret
}
const ONCE = '~';
const CUSTOM = '^';
function handleEvent (event) {
event = wrapper$1(event);
// [['tap',[['handle',[1,2,a]],['handle1',[1,2,a]]]]]
const eventOpts = (event.currentTarget || event.target).dataset.eventOpts;
if (!eventOpts) {
return console.warn(`事件信息不存在`)
}
// [['handle',[1,2,a]],['handle1',[1,2,a]]]
const eventType = event.type;
eventOpts.forEach(eventOpt => {
let type = eventOpt[0];
const eventsArray = eventOpt[1];
const isCustom = type.charAt(0) === CUSTOM;
type = isCustom ? type.slice(1) : type;
const isOnce = type.charAt(0) === ONCE;
type = isOnce ? type.slice(1) : type;
if (eventsArray && eventType === type) {
eventsArray.forEach(eventArray => {
const handler = this.$vm[eventArray[0]];
if (!isFn(handler)) {
throw new Error(` _vm.${eventArray[0]} is not a function`)
}
if (isOnce) {
if (handler.once) {
return
}
handler.once = true;
}
handler.apply(this.$vm, processEventArgs(event, eventArray[1], isCustom));
});
}
});
}
function handleLink (event) {
event.detail.$parent = this.$vm;
}
function initRefs (vm) {
const mpInstance = vm.$mp[vm.mpType];
Object.defineProperty(vm, '$refs', {
get () {
const $refs = Object.create(null);
const components = mpInstance.selectAllComponents('.vue-ref');
components.forEach(component => {
const ref = component.dataset.ref;
$refs[ref] = component.$vm;
});
const forComponents = mpInstance.selectAllComponents('.vue-ref-in-for');
forComponents.forEach(component => {
const ref = component.dataset.ref;
if (!$refs[ref]) {
$refs[ref] = [];
}
$refs[ref].push(component.$vm);
});
return $refs
}
});
}
function initChildren (vm) {
const mpInstance = vm.$mp[vm.mpType];
Object.defineProperty(vm, '$children', {
get () {
const $children = [];
const components = mpInstance.selectAllComponents('.vue-com');
components.forEach(component => {
$children.push(component.$vm);
});
return $children
}
});
}
const hooks = [
'onShow',
'onHide',
'onError',
'onPageNotFound'
];
function createApp (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
// 外部初始化时 Vue 还未初始化,放到 createApp 内部初始化 mixin
Vue.mixin({
beforeCreate () {
if (!this.$options.mpType) {
return
}
this.mpType = this.$options.mpType;
this.$mp = {
data: {},
[this.mpType]: this.$options.mpInstance
};
delete this.$options.mpType;
delete this.$options.mpInstance;
if (this.mpType !== 'app') {
initRefs(this);
initMocks(this);
initChildren(this);
}
}
});
const appOptions = {
onLaunch (args) {
this.$vm = new Vue(Object.assign(vueOptions, {
mpType: 'app',
mpInstance: this
}));
this.$vm.$mount();
setTimeout(() => this.$vm.__call_hook('onLaunch', args));
}
};
initHooks(appOptions, hooks, true); // 延迟执行,因为 App 的注册在 main.js 之前,可能导致生命周期内 Vue 原型上开发者注册的属性无法访问
App(appOptions);
return vueOptions
}
const hooks$1 = [
'onShow',
'onHide',
'onPullDownRefresh',
'onReachBottom',
'onShareAppMessage',
'onPageScroll',
'onResize',
'onTabItemTap',
'onBackPress',
'onNavigationBarButtonTap',
'onNavigationBarSearchInputChanged',
'onNavigationBarSearchInputConfirmed',
'onNavigationBarSearchInputClicked'
];
function createPage (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
const pageOptions = {
data: getData(vueOptions),
onLoad (args) {
this.$vm = new Vue(Object.assign(vueOptions, {
mpType: 'page',
mpInstance: this
}));
this.$vm.$mount();
this.$vm.__call_hook('onLoad', args);
},
onReady () {
this.$vm._isMounted = true;
this.$vm.__call_hook('onReady');
},
onUnload () {
this.$vm.__call_hook('onUnload');
{
this.$vm.$destroy();
}
},
__e: handleEvent,
__l: handleLink
};
initHooks(pageOptions, hooks$1);
return Page(pageOptions)
}
function initVueComponent (mpInstace, VueComponent) {
if (mpInstace.$vm) {
return
}
const options = {
mpType: 'component',
mpInstance: mpInstace,
propsData: mpInstace.properties
};
// 初始化 vue 实例
mpInstace.$vm = new VueComponent(options);
// 初始化渲染数据
mpInstace.$vm.$mount();
}
function createComponent (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
const properties = getProperties(vueOptions.props);
const VueComponent = Vue.extend(vueOptions);
const componentOptions = {
options: {
multipleSlots: true,
addGlobalClass: true
},
data: getData(vueOptions),
properties,
lifetimes: {
attached () {
initVueComponent(this, VueComponent);
},
ready () {
initVueComponent(this, VueComponent); // 目前发现部分情况小程序 attached 不触发
{
this.triggerEvent('__l', this.$vm); // TODO 百度仅能传递 json 对象
}
const eventId = this.dataset.eventId;
if (eventId) {
const listeners = this.$vm.$parent.$mp.listeners;
if (listeners) {
const listenerOpts = listeners[eventId];
Object.keys(listenerOpts).forEach(eventType => {
listenerOpts[eventType].forEach(handler => {
this.$vm[handler.once ? '$once' : '$on'](eventType, handler);
});
});
}
}
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady');
},
detached () {
this.$vm.$destroy();
}
},
pageLifetimes: {
show (args) {
this.$vm.__call_hook('onPageShow', args);
},
hide () {
this.$vm.__call_hook('onPageHide');
},
resize (size) {
this.$vm.__call_hook('onPageResize', size);
}
},
methods: {
__e: handleEvent,
__l: handleLink
}
};
return Component(componentOptions)
}
let uni = {}; let uni = {};
if (typeof Proxy !== 'undefined') { if (typeof Proxy !== 'undefined') {
...@@ -470,3 +848,4 @@ if (typeof Proxy !== 'undefined') { ...@@ -470,3 +848,4 @@ if (typeof Proxy !== 'undefined') {
var uni$1 = uni; var uni$1 = uni;
export default uni$1; export default uni$1;
export { createApp, createPage, createComponent };
{ {
"name": "@dcloudio/uni-mp-toutiao", "name": "@dcloudio/uni-mp-toutiao",
"version": "0.0.3", "version": "0.0.301",
"description": "uni-app mp-toutiao", "description": "uni-app mp-toutiao",
"main": "dist/index.js", "main": "dist/index.js",
"scripts": { "scripts": {
......
...@@ -279,33 +279,51 @@ function initMocks (vm) { ...@@ -279,33 +279,51 @@ function initMocks (vm) {
}); });
} }
function initHooks (mpOptions, hooks) { function initHooks (mpOptions, hooks, delay = false) {
hooks.forEach(hook => { hooks.forEach(hook => {
mpOptions[hook] = function (args) { mpOptions[hook] = function (args) {
this.$vm.__call_hook(hook, args); if (delay) {
setTimeout(() => this.$vm.__call_hook(hook, args));
} else {
this.$vm.__call_hook(hook, args);
}
}; };
}); });
} }
function getData (data) { function getData (vueOptions) {
let data = vueOptions.data || {};
const methods = vueOptions.methods || {};
if (typeof data === 'function') { if (typeof data === 'function') {
try { try {
return data() data = data();
} catch (e) { } catch (e) {
console.warn('根据 Vue 的 data 函数初始化小程序 data 失败,请尽量确保 data 函数中不访问 vm 对象,否则可能影响首次数据渲染速度。'); console.warn('根据 Vue 的 data 函数初始化小程序 data 失败,请尽量确保 data 函数中不访问 vm 对象,否则可能影响首次数据渲染速度。', data);
} }
return {}
} }
return data || {}
return Object.assign(data, methods)
} }
const PROP_TYPES = [String, Number, Boolean, Object, Array, null]; const PROP_TYPES = [String, Number, Boolean, Object, Array, null];
function createObserver (name) {
return function observer (newVal, oldVal) {
if (this.$vm) {
this.$vm[name] = newVal; // 为了触发其他非 render watcher
}
}
}
function getProperties (props) { function getProperties (props) {
const properties = {}; const properties = {};
if (Array.isArray(props)) { // ['title'] if (Array.isArray(props)) { // ['title']
props.forEach(key => { props.forEach(key => {
properties[key] = null; properties[key] = {
type: null,
observer: createObserver(key)
};
}); });
} else if (isPlainObject(props)) { // {title:{type:String,default:''},content:String} } else if (isPlainObject(props)) { // {title:{type:String,default:''},content:String}
Object.keys(props).forEach(key => { Object.keys(props).forEach(key => {
...@@ -317,10 +335,14 @@ function getProperties (props) { ...@@ -317,10 +335,14 @@ function getProperties (props) {
} }
properties[key] = { properties[key] = {
type: PROP_TYPES.includes(opts.type) ? opts.type : null, type: PROP_TYPES.includes(opts.type) ? opts.type : null,
value value,
observer: createObserver(key)
}; };
} else { // content:String } else { // content:String
properties[key] = PROP_TYPES.includes(opts) ? opts : null; properties[key] = {
type: PROP_TYPES.includes(opts) ? opts : null,
observer: createObserver(key)
};
} }
}); });
} }
...@@ -333,6 +355,7 @@ function wrapper$1 (event) { ...@@ -333,6 +355,7 @@ function wrapper$1 (event) {
event.target = event.target || {}; event.target = event.target || {};
event.detail = event.detail || {}; event.detail = event.detail || {};
// TODO 又得兼容 mpvue 的 mp 对象 // TODO 又得兼容 mpvue 的 mp 对象
event.mp = event; event.mp = event;
event.target = Object.assign({}, event.target, event.detail); event.target = Object.assign({}, event.target, event.detail);
...@@ -381,6 +404,9 @@ function handleEvent (event) { ...@@ -381,6 +404,9 @@ function handleEvent (event) {
if (eventsArray && eventType === type) { if (eventsArray && eventType === type) {
eventsArray.forEach(eventArray => { eventsArray.forEach(eventArray => {
const handler = this.$vm[eventArray[0]]; const handler = this.$vm[eventArray[0]];
if (!isFn(handler)) {
throw new Error(` _vm.${eventArray[0]} is not a function`)
}
if (isOnce) { if (isOnce) {
if (handler.once) { if (handler.once) {
return return
...@@ -402,12 +428,12 @@ function initRefs (vm) { ...@@ -402,12 +428,12 @@ function initRefs (vm) {
Object.defineProperty(vm, '$refs', { Object.defineProperty(vm, '$refs', {
get () { get () {
const $refs = Object.create(null); const $refs = Object.create(null);
const components = mpInstance.selectAllComponents('.__ref__'); const components = mpInstance.selectAllComponents('.vue-ref');
components.forEach(component => { components.forEach(component => {
const ref = component.dataset.ref; const ref = component.dataset.ref;
$refs[ref] = component.$vm; $refs[ref] = component.$vm;
}); });
const forComponents = mpInstance.selectAllComponents('.__ref-in-for__'); const forComponents = mpInstance.selectAllComponents('.vue-ref-in-for');
forComponents.forEach(component => { forComponents.forEach(component => {
const ref = component.dataset.ref; const ref = component.dataset.ref;
if (!$refs[ref]) { if (!$refs[ref]) {
...@@ -418,6 +444,20 @@ function initRefs (vm) { ...@@ -418,6 +444,20 @@ function initRefs (vm) {
return $refs return $refs
} }
}); });
}
function initChildren (vm) {
const mpInstance = vm.$mp[vm.mpType];
Object.defineProperty(vm, '$children', {
get () {
const $children = [];
const components = mpInstance.selectAllComponents('.vue-com');
components.forEach(component => {
$children.push(component.$vm);
});
return $children
}
});
} }
const hooks = [ const hooks = [
...@@ -429,20 +469,41 @@ const hooks = [ ...@@ -429,20 +469,41 @@ const hooks = [
function createApp (vueOptions) { function createApp (vueOptions) {
vueOptions = vueOptions.default || vueOptions; vueOptions = vueOptions.default || vueOptions;
// 外部初始化时 Vue 还未初始化,放到 createApp 内部初始化 mixin
Vue.mixin({
beforeCreate () {
if (!this.$options.mpType) {
return
}
this.mpType = this.$options.mpType;
this.$mp = {
data: {},
[this.mpType]: this.$options.mpInstance
};
delete this.$options.mpType;
delete this.$options.mpInstance;
if (this.mpType !== 'app') {
initRefs(this);
initMocks(this);
initChildren(this);
}
}
});
const appOptions = { const appOptions = {
onLaunch (args) { onLaunch (args) {
this.$vm = new Vue(vueOptions); this.$vm = new Vue(Object.assign(vueOptions, {
this.$vm.mpType = 'app'; mpType: 'app',
this.$vm.$mp = { mpInstance: this
app: this }));
};
this.$vm.$mount(); this.$vm.$mount();
this.$vm.__call_hook('onLaunch', args); setTimeout(() => this.$vm.__call_hook('onLaunch', args));
} }
}; };
initHooks(appOptions, hooks); initHooks(appOptions, hooks, true); // 延迟执行,因为 App 的注册在 main.js 之前,可能导致生命周期内 Vue 原型上开发者注册的属性无法访问
App(appOptions); App(appOptions);
...@@ -468,17 +529,13 @@ const hooks$1 = [ ...@@ -468,17 +529,13 @@ const hooks$1 = [
function createPage (vueOptions) { function createPage (vueOptions) {
vueOptions = vueOptions.default || vueOptions; vueOptions = vueOptions.default || vueOptions;
const pageOptions = { const pageOptions = {
data: getData(vueOptions.data), data: getData(vueOptions),
onLoad (args) { onLoad (args) {
this.$vm = new Vue(vueOptions);
this.$vm.mpType = 'page';
this.$vm.$mp = {
data: {},
page: this
};
initRefs(this.$vm); this.$vm = new Vue(Object.assign(vueOptions, {
initMocks(this.$vm); mpType: 'page',
mpInstance: this
}));
this.$vm.$mount(); this.$vm.$mount();
this.$vm.__call_hook('onLoad', args); this.$vm.__call_hook('onLoad', args);
...@@ -489,7 +546,9 @@ function createPage (vueOptions) { ...@@ -489,7 +546,9 @@ function createPage (vueOptions) {
}, },
onUnload () { onUnload () {
this.$vm.__call_hook('onUnload'); this.$vm.__call_hook('onUnload');
this.$vm.$destroy(); {
this.$vm.$destroy();
}
}, },
__e: handleEvent, __e: handleEvent,
__l: handleLink __l: handleLink
...@@ -500,6 +559,23 @@ function createPage (vueOptions) { ...@@ -500,6 +559,23 @@ function createPage (vueOptions) {
return Page(pageOptions) return Page(pageOptions)
} }
function initVueComponent (mpInstace, VueComponent) {
if (mpInstace.$vm) {
return
}
const options = {
mpType: 'component',
mpInstance: mpInstace,
propsData: mpInstace.properties
};
// 初始化 vue 实例
mpInstace.$vm = new VueComponent(options);
// 初始化渲染数据
mpInstace.$vm.$mount();
}
function createComponent (vueOptions) { function createComponent (vueOptions) {
vueOptions = vueOptions.default || vueOptions; vueOptions = vueOptions.default || vueOptions;
...@@ -512,50 +588,39 @@ function createComponent (vueOptions) { ...@@ -512,50 +588,39 @@ function createComponent (vueOptions) {
multipleSlots: true, multipleSlots: true,
addGlobalClass: true addGlobalClass: true
}, },
data: getData(vueOptions.data), data: getData(vueOptions),
properties, properties,
attached () { lifetimes: {
// props的处理,一个是直接 与 mp 的 properties 对接,另一个是要做成 reactive,且排除掉 render watch attached () {
const options = { initVueComponent(this, VueComponent);
propsData: this.properties, },
$component: this ready () {
}; initVueComponent(this, VueComponent); // 目前发现部分情况小程序 attached 不触发
// 初始化 vue 实例
this.$vm = new VueComponent(options);
this.$vm.mpType = 'component';
this.$vm.$mp = {
data: {},
component: this
};
initRefs(this.$vm); {
initMocks(this.$vm); this.triggerEvent('__l', this.$vm); // TODO 百度仅能传递 json 对象
}
// 初始化渲染数据 const eventId = this.dataset.eventId;
this.$vm.$mount(); if (eventId) {
}, const listeners = this.$vm.$parent.$mp.listeners;
ready () { if (listeners) {
this.triggerEvent('__l', this.$vm); const listenerOpts = listeners[eventId];
Object.keys(listenerOpts).forEach(eventType => {
const eventId = this.dataset.eventId; listenerOpts[eventType].forEach(handler => {
if (eventId) { this.$vm[handler.once ? '$once' : '$on'](eventType, handler);
const listeners = this.$vm.$parent.$mp.listeners; });
if (listeners) {
const listenerOpts = listeners[eventId];
Object.keys(listenerOpts).forEach(eventType => {
listenerOpts[eventType].forEach(handler => {
this.$vm[handler.once ? '$once' : '$on'](eventType, handler);
}); });
}); }
} }
}
this.$vm._isMounted = true; this.$vm._isMounted = true;
this.$vm.__call_hook('mounted'); this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady'); this.$vm.__call_hook('onReady');
}, },
detached () { detached () {
this.$vm.$destroy(); this.$vm.$destroy();
}
}, },
pageLifetimes: { pageLifetimes: {
show (args) { show (args) {
......
{ {
"name": "@dcloudio/uni-mp-weixin", "name": "@dcloudio/uni-mp-weixin",
"version": "0.0.8", "version": "0.0.905",
"description": "uni-app mp-weixin", "description": "uni-app mp-weixin",
"main": "dist/index.js", "main": "dist/index.js",
"scripts": { "scripts": {
......
import Vue from 'vue' import Vue from 'vue'
import { import {
initHooks initRefs,
initHooks,
initMocks,
initChildren
} from './util' } from './util'
const hooks = [ const hooks = [
...@@ -13,20 +16,41 @@ const hooks = [ ...@@ -13,20 +16,41 @@ const hooks = [
export function createApp (vueOptions) { export function createApp (vueOptions) {
vueOptions = vueOptions.default || vueOptions vueOptions = vueOptions.default || vueOptions
// 外部初始化时 Vue 还未初始化,放到 createApp 内部初始化 mixin
Vue.mixin({
beforeCreate () {
if (!this.$options.mpType) {
return
}
this.mpType = this.$options.mpType
this.$mp = {
data: {},
[this.mpType]: this.$options.mpInstance
}
delete this.$options.mpType
delete this.$options.mpInstance
if (this.mpType !== 'app') {
initRefs(this)
initMocks(this)
initChildren(this)
}
}
})
const appOptions = { const appOptions = {
onLaunch (args) { onLaunch (args) {
this.$vm = new Vue(vueOptions) this.$vm = new Vue(Object.assign(vueOptions, {
this.$vm.mpType = 'app' mpType: 'app',
this.$vm.$mp = { mpInstance: this
app: this }))
}
this.$vm.$mount() this.$vm.$mount()
this.$vm.__call_hook('onLaunch', args) setTimeout(() => this.$vm.__call_hook('onLaunch', args))
} }
} }
initHooks(appOptions, hooks) initHooks(appOptions, hooks, true) // 延迟执行,因为 App 的注册在 main.js 之前,可能导致生命周期内 Vue 原型上开发者注册的属性无法访问
App(appOptions) App(appOptions)
......
...@@ -2,14 +2,28 @@ import Vue from 'vue' ...@@ -2,14 +2,28 @@ import Vue from 'vue'
import { import {
getData, getData,
initRefs,
initMocks,
initMethods,
handleLink, handleLink,
handleEvent, handleEvent,
getProperties getProperties
} from './util' } from './util'
function initVueComponent (mpInstace, VueComponent) {
if (mpInstace.$vm) {
return
}
const options = {
mpType: 'component',
mpInstance: mpInstace,
propsData: mpInstace.properties
}
// 初始化 vue 实例
mpInstace.$vm = new VueComponent(options)
// 初始化渲染数据
mpInstace.$vm.$mount()
}
export function createComponent (vueOptions) { export function createComponent (vueOptions) {
vueOptions = vueOptions.default || vueOptions vueOptions = vueOptions.default || vueOptions
...@@ -22,50 +36,56 @@ export function createComponent (vueOptions) { ...@@ -22,50 +36,56 @@ export function createComponent (vueOptions) {
multipleSlots: true, multipleSlots: true,
addGlobalClass: true addGlobalClass: true
}, },
data: getData(vueOptions.data), data: getData(vueOptions),
properties, properties,
attached () { lifetimes: {
// props的处理,一个是直接 与 mp 的 properties 对接,另一个是要做成 reactive,且排除掉 render watch attached () {
const options = { initVueComponent(this, VueComponent)
propsData: this.properties, },
$component: this ready () {
} initVueComponent(this, VueComponent) // 目前发现部分情况小程序 attached 不触发
// 初始化 vue 实例
this.$vm = new VueComponent(options)
this.$vm.mpType = 'component'
this.$vm.$mp = {
data: {},
component: this
}
initRefs(this.$vm) if (__PLATFORM__ === 'mp-baidu') {
initMocks(this.$vm) const baiduComponentInstances = this.pageinstance.$baiduComponentInstances
// 初始化渲染数据 baiduComponentInstances[this.id] = this
this.$vm.$mount() if (this.ownerId) { // 组件嵌组件
}, const parentBaiduComponentInstance = baiduComponentInstances[this.ownerId]
ready () { if (parentBaiduComponentInstance) {
this.triggerEvent('__l', this.$vm) this.$vm.$parent = parentBaiduComponentInstance.$vm
} else {
console.error(`查找父组件失败${this.ownerId}`)
}
} else { // 页面直属组件
this.$vm.$parent = this.pageinstance.$vm
}
} else {
this.triggerEvent('__l', this.$vm) // TODO 百度仅能传递 json 对象
}
const eventId = this.dataset.eventId const eventId = this.dataset.eventId
if (eventId) { if (eventId) {
const listeners = this.$vm.$parent.$mp.listeners const listeners = this.$vm.$parent.$mp.listeners
if (listeners) { if (listeners) {
const listenerOpts = listeners[eventId] const listenerOpts = listeners[eventId]
Object.keys(listenerOpts).forEach(eventType => { Object.keys(listenerOpts).forEach(eventType => {
listenerOpts[eventType].forEach(handler => { listenerOpts[eventType].forEach(handler => {
this.$vm[handler.once ? '$once' : '$on'](eventType, handler) this.$vm[handler.once ? '$once' : '$on'](eventType, handler)
})
}) })
}) }
} }
}
this.$vm._isMounted = true this.$vm._isMounted = true
this.$vm.__call_hook('mounted') this.$vm.__call_hook('mounted')
this.$vm.__call_hook('onReady') this.$vm.__call_hook('onReady')
}, },
detached () { detached () {
this.$vm.$destroy() if (__PLATFORM__ === 'mp-baidu') {
delete this.pageinstance.$baiduComponentInstances[this.id]
}
this.$vm.$destroy()
}
}, },
pageLifetimes: { pageLifetimes: {
show (args) { show (args) {
...@@ -84,7 +104,5 @@ export function createComponent (vueOptions) { ...@@ -84,7 +104,5 @@ export function createComponent (vueOptions) {
} }
} }
initMethods(componentOptions.methods, vueOptions)
return Component(componentOptions) return Component(componentOptions)
} }
...@@ -2,12 +2,10 @@ import Vue from 'vue' ...@@ -2,12 +2,10 @@ import Vue from 'vue'
import { import {
getData, getData,
initRefs,
initHooks, initHooks,
initMocks,
initMethods,
handleLink, handleLink,
handleEvent handleEvent,
baiduPageDestroy
} from './util' } from './util'
const hooks = [ const hooks = [
...@@ -29,17 +27,16 @@ const hooks = [ ...@@ -29,17 +27,16 @@ const hooks = [
export function createPage (vueOptions) { export function createPage (vueOptions) {
vueOptions = vueOptions.default || vueOptions vueOptions = vueOptions.default || vueOptions
const pageOptions = { const pageOptions = {
data: getData(vueOptions.data), data: getData(vueOptions),
onLoad (args) { onLoad (args) {
this.$vm = new Vue(vueOptions) if (__PLATFORM__ === 'mp-baidu') {
this.$vm.mpType = 'page' this.$baiduComponentInstances = Object.create(null)
this.$vm.$mp = {
data: {},
page: this
} }
initRefs(this.$vm) this.$vm = new Vue(Object.assign(vueOptions, {
initMocks(this.$vm) mpType: 'page',
mpInstance: this
}))
this.$vm.$mount() this.$vm.$mount()
this.$vm.__call_hook('onLoad', args) this.$vm.__call_hook('onLoad', args)
...@@ -50,7 +47,11 @@ export function createPage (vueOptions) { ...@@ -50,7 +47,11 @@ export function createPage (vueOptions) {
}, },
onUnload () { onUnload () {
this.$vm.__call_hook('onUnload') this.$vm.__call_hook('onUnload')
this.$vm.$destroy() if (__PLATFORM__ === 'mp-baidu') { // 百度组件不会在页面 unload 时触发 detached
baiduPageDestroy(this.$vm)
} else {
this.$vm.$destroy()
}
}, },
__e: handleEvent, __e: handleEvent,
__l: handleLink __l: handleLink
...@@ -58,7 +59,5 @@ export function createPage (vueOptions) { ...@@ -58,7 +59,5 @@ export function createPage (vueOptions) {
initHooks(pageOptions, hooks) initHooks(pageOptions, hooks)
initMethods(pageOptions, vueOptions)
return Page(pageOptions) return Page(pageOptions)
} }
...@@ -16,39 +16,51 @@ export function initMocks (vm) { ...@@ -16,39 +16,51 @@ export function initMocks (vm) {
}) })
} }
export function initHooks (mpOptions, hooks) { export function initHooks (mpOptions, hooks, delay = false) {
hooks.forEach(hook => { hooks.forEach(hook => {
mpOptions[hook] = function (args) { mpOptions[hook] = function (args) {
this.$vm.__call_hook(hook, args) if (delay) {
setTimeout(() => this.$vm.__call_hook(hook, args))
} else {
this.$vm.__call_hook(hook, args)
}
} }
}) })
} }
export function initMethods (mpOptions, vueOptions) { export function getData (vueOptions) {
// if (vueOptions.methods) { let data = vueOptions.data || {}
// Object.assign(mpOptions, vueOptions.methods) const methods = vueOptions.methods || {}
// }
}
export function getData (data) {
if (typeof data === 'function') { if (typeof data === 'function') {
try { try {
return data() data = data()
} catch (e) { } catch (e) {
console.warn('根据 Vue 的 data 函数初始化小程序 data 失败,请尽量确保 data 函数中不访问 vm 对象,否则可能影响首次数据渲染速度。') console.warn('根据 Vue 的 data 函数初始化小程序 data 失败,请尽量确保 data 函数中不访问 vm 对象,否则可能影响首次数据渲染速度。', data)
} }
return {}
} }
return data || {}
return Object.assign(data, methods)
} }
const PROP_TYPES = [String, Number, Boolean, Object, Array, null] const PROP_TYPES = [String, Number, Boolean, Object, Array, null]
function createObserver (name) {
return function observer (newVal, oldVal) {
if (this.$vm) {
this.$vm[name] = newVal // 为了触发其他非 render watcher
}
}
}
export function getProperties (props) { export function getProperties (props) {
const properties = {} const properties = {}
if (Array.isArray(props)) { // ['title'] if (Array.isArray(props)) { // ['title']
props.forEach(key => { props.forEach(key => {
properties[key] = null properties[key] = {
type: null,
observer: createObserver(key)
}
}) })
} else if (isPlainObject(props)) { // {title:{type:String,default:''},content:String} } else if (isPlainObject(props)) { // {title:{type:String,default:''},content:String}
Object.keys(props).forEach(key => { Object.keys(props).forEach(key => {
...@@ -60,10 +72,14 @@ export function getProperties (props) { ...@@ -60,10 +72,14 @@ export function getProperties (props) {
} }
properties[key] = { properties[key] = {
type: PROP_TYPES.includes(opts.type) ? opts.type : null, type: PROP_TYPES.includes(opts.type) ? opts.type : null,
value value,
observer: createObserver(key)
} }
} else { // content:String } else { // content:String
properties[key] = PROP_TYPES.includes(opts) ? opts : null properties[key] = {
type: PROP_TYPES.includes(opts) ? opts : null,
observer: createObserver(key)
}
} }
}) })
} }
...@@ -76,6 +92,13 @@ function wrapper (event) { ...@@ -76,6 +92,13 @@ function wrapper (event) {
event.target = event.target || {} event.target = event.target || {}
event.detail = event.detail || {} event.detail = event.detail || {}
if (__PLATFORM__ === 'mp-baidu') { // mp-baidu,checked=>value
if (hasOwn(event.detail, 'checked') && !hasOwn(event.detail, 'value')) {
event.detail.value = event.detail.checked
}
}
// TODO 又得兼容 mpvue 的 mp 对象 // TODO 又得兼容 mpvue 的 mp 对象
event.mp = event event.mp = event
event.target = Object.assign({}, event.target, event.detail) event.target = Object.assign({}, event.target, event.detail)
...@@ -124,6 +147,9 @@ export function handleEvent (event) { ...@@ -124,6 +147,9 @@ export function handleEvent (event) {
if (eventsArray && eventType === type) { if (eventsArray && eventType === type) {
eventsArray.forEach(eventArray => { eventsArray.forEach(eventArray => {
const handler = this.$vm[eventArray[0]] const handler = this.$vm[eventArray[0]]
if (!isFn(handler)) {
throw new Error(` _vm.${eventArray[0]} is not a function`)
}
if (isOnce) { if (isOnce) {
if (handler.once) { if (handler.once) {
return return
...@@ -145,12 +171,12 @@ export function initRefs (vm) { ...@@ -145,12 +171,12 @@ export function initRefs (vm) {
Object.defineProperty(vm, '$refs', { Object.defineProperty(vm, '$refs', {
get () { get () {
const $refs = Object.create(null) const $refs = Object.create(null)
const components = mpInstance.selectAllComponents('.__ref__') const components = mpInstance.selectAllComponents('.vue-ref')
components.forEach(component => { components.forEach(component => {
const ref = component.dataset.ref const ref = component.dataset.ref
$refs[ref] = component.$vm $refs[ref] = component.$vm
}) })
const forComponents = mpInstance.selectAllComponents('.__ref-in-for__') const forComponents = mpInstance.selectAllComponents('.vue-ref-in-for')
forComponents.forEach(component => { forComponents.forEach(component => {
const ref = component.dataset.ref const ref = component.dataset.ref
if (!$refs[ref]) { if (!$refs[ref]) {
...@@ -161,4 +187,32 @@ export function initRefs (vm) { ...@@ -161,4 +187,32 @@ export function initRefs (vm) {
return $refs return $refs
} }
}) })
}
export function initChildren (vm) {
const mpInstance = vm.$mp[vm.mpType]
Object.defineProperty(vm, '$children', {
get () {
const $children = []
const components = mpInstance.selectAllComponents('.vue-com')
components.forEach(component => {
$children.push(component.$vm)
})
return $children
}
})
}
function baiduComponentDestroy ($vm) {
$vm.$children.forEach(childVm => {
childVm.$mp.component.detached()
})
$vm.$mp.component.detached()
}
export function baiduPageDestroy ($vm) {
$vm.$destroy()
$vm.$children.forEach(childVm => {
baiduComponentDestroy(childVm)
})
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册