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

feat(runtime): uni-mp

上级 a9e781aa
......@@ -40,7 +40,8 @@ module.exports = {
}),
replace({
__GLOBAL__: platform.prefix,
__PLATFORM_TITLE__: platform.title
__PLATFORM_TITLE__: platform.title,
__PLATFORM__: JSON.stringify(process.env.UNI_PLATFORM)
})
],
external: ['vue']
......
import Vue from 'vue';
const _toString = Object.prototype.toString;
const hasOwnProperty = Object.prototype.hasOwnProperty;
......@@ -15,7 +17,9 @@ function isPlainObject (obj) {
function hasOwn (obj, key) {
return hasOwnProperty.call(obj, key)
}
}
function noop () {}
const SYNC_API_RE = /hideKeyboard|upx2px|canIUse|^create|Sync$|Manager$/;
......@@ -209,6 +213,9 @@ const protocols = {
},
navigateBackMiniProgram: {
name: 'navigateBackSmartProgram'
},
showShareMenu: {
name: 'openShare'
}
};
......@@ -374,6 +381,418 @@ var api = /*#__PURE__*/Object.freeze({
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 = {};
if (typeof Proxy !== 'undefined') {
......@@ -422,3 +841,4 @@ if (typeof Proxy !== 'undefined') {
var uni$1 = uni;
export default uni$1;
export { createApp, createPage, createComponent };
{
"name": "@dcloudio/uni-mp-baidu",
"version": "0.0.7",
"version": "0.0.804",
"description": "uni-app mp-baidu",
"main": "dist/index.js",
"scripts": {
......
import Vue from 'vue';
const _toString = Object.prototype.toString;
const hasOwnProperty = Object.prototype.hasOwnProperty;
......@@ -15,7 +17,9 @@ function isPlainObject (obj) {
function hasOwn (obj, key) {
return hasOwnProperty.call(obj, key)
}
}
function noop () {}
const SYNC_API_RE = /hideKeyboard|upx2px|canIUse|^create|Sync$|Manager$/;
......@@ -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 = {};
if (typeof Proxy !== 'undefined') {
......@@ -470,3 +848,4 @@ if (typeof Proxy !== 'undefined') {
var uni$1 = uni;
export default uni$1;
export { createApp, createPage, createComponent };
{
"name": "@dcloudio/uni-mp-toutiao",
"version": "0.0.3",
"version": "0.0.301",
"description": "uni-app mp-toutiao",
"main": "dist/index.js",
"scripts": {
......
......@@ -279,33 +279,51 @@ function initMocks (vm) {
});
}
function initHooks (mpOptions, hooks) {
function initHooks (mpOptions, hooks, delay = false) {
hooks.forEach(hook => {
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') {
try {
return data()
data = data();
} 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];
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] = null;
properties[key] = {
type: null,
observer: createObserver(key)
};
});
} else if (isPlainObject(props)) { // {title:{type:String,default:''},content:String}
Object.keys(props).forEach(key => {
......@@ -317,10 +335,14 @@ function getProperties (props) {
}
properties[key] = {
type: PROP_TYPES.includes(opts.type) ? opts.type : null,
value
value,
observer: createObserver(key)
};
} 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) {
event.target = event.target || {};
event.detail = event.detail || {};
// TODO 又得兼容 mpvue 的 mp 对象
event.mp = event;
event.target = Object.assign({}, event.target, event.detail);
......@@ -381,6 +404,9 @@ function handleEvent (event) {
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
......@@ -402,12 +428,12 @@ function initRefs (vm) {
Object.defineProperty(vm, '$refs', {
get () {
const $refs = Object.create(null);
const components = mpInstance.selectAllComponents('.__ref__');
const components = mpInstance.selectAllComponents('.vue-ref');
components.forEach(component => {
const ref = component.dataset.ref;
$refs[ref] = component.$vm;
});
const forComponents = mpInstance.selectAllComponents('.__ref-in-for__');
const forComponents = mpInstance.selectAllComponents('.vue-ref-in-for');
forComponents.forEach(component => {
const ref = component.dataset.ref;
if (!$refs[ref]) {
......@@ -418,6 +444,20 @@ function initRefs (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 = [
......@@ -429,20 +469,41 @@ const hooks = [
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(vueOptions);
this.$vm.mpType = 'app';
this.$vm.$mp = {
app: this
};
this.$vm = new Vue(Object.assign(vueOptions, {
mpType: 'app',
mpInstance: this
}));
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);
......@@ -468,17 +529,13 @@ const hooks$1 = [
function createPage (vueOptions) {
vueOptions = vueOptions.default || vueOptions;
const pageOptions = {
data: getData(vueOptions.data),
data: getData(vueOptions),
onLoad (args) {
this.$vm = new Vue(vueOptions);
this.$vm.mpType = 'page';
this.$vm.$mp = {
data: {},
page: this
};
initRefs(this.$vm);
initMocks(this.$vm);
this.$vm = new Vue(Object.assign(vueOptions, {
mpType: 'page',
mpInstance: this
}));
this.$vm.$mount();
this.$vm.__call_hook('onLoad', args);
......@@ -489,7 +546,9 @@ function createPage (vueOptions) {
},
onUnload () {
this.$vm.__call_hook('onUnload');
this.$vm.$destroy();
{
this.$vm.$destroy();
}
},
__e: handleEvent,
__l: handleLink
......@@ -500,6 +559,23 @@ function createPage (vueOptions) {
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;
......@@ -512,50 +588,39 @@ function createComponent (vueOptions) {
multipleSlots: true,
addGlobalClass: true
},
data: getData(vueOptions.data),
data: getData(vueOptions),
properties,
attached () {
// props的处理,一个是直接 与 mp 的 properties 对接,另一个是要做成 reactive,且排除掉 render watch
const options = {
propsData: this.properties,
$component: this
};
// 初始化 vue 实例
this.$vm = new VueComponent(options);
this.$vm.mpType = 'component';
this.$vm.$mp = {
data: {},
component: this
};
lifetimes: {
attached () {
initVueComponent(this, VueComponent);
},
ready () {
initVueComponent(this, VueComponent); // 目前发现部分情况小程序 attached 不触发
initRefs(this.$vm);
initMocks(this.$vm);
{
this.triggerEvent('__l', this.$vm); // TODO 百度仅能传递 json 对象
}
// 初始化渲染数据
this.$vm.$mount();
},
ready () {
this.triggerEvent('__l', this.$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);
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();
this.$vm._isMounted = true;
this.$vm.__call_hook('mounted');
this.$vm.__call_hook('onReady');
},
detached () {
this.$vm.$destroy();
}
},
pageLifetimes: {
show (args) {
......
{
"name": "@dcloudio/uni-mp-weixin",
"version": "0.0.8",
"version": "0.0.905",
"description": "uni-app mp-weixin",
"main": "dist/index.js",
"scripts": {
......
import Vue from 'vue'
import {
initHooks
initRefs,
initHooks,
initMocks,
initChildren
} from './util'
const hooks = [
......@@ -13,20 +16,41 @@ const hooks = [
export 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(vueOptions)
this.$vm.mpType = 'app'
this.$vm.$mp = {
app: this
}
this.$vm = new Vue(Object.assign(vueOptions, {
mpType: 'app',
mpInstance: this
}))
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)
......
......@@ -2,14 +2,28 @@ import Vue from 'vue'
import {
getData,
initRefs,
initMocks,
initMethods,
handleLink,
handleEvent,
getProperties
} 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) {
vueOptions = vueOptions.default || vueOptions
......@@ -22,50 +36,56 @@ export function createComponent (vueOptions) {
multipleSlots: true,
addGlobalClass: true
},
data: getData(vueOptions.data),
data: getData(vueOptions),
properties,
attached () {
// props的处理,一个是直接 与 mp 的 properties 对接,另一个是要做成 reactive,且排除掉 render watch
const options = {
propsData: this.properties,
$component: this
}
// 初始化 vue 实例
this.$vm = new VueComponent(options)
this.$vm.mpType = 'component'
this.$vm.$mp = {
data: {},
component: this
}
lifetimes: {
attached () {
initVueComponent(this, VueComponent)
},
ready () {
initVueComponent(this, VueComponent) // 目前发现部分情况小程序 attached 不触发
initRefs(this.$vm)
initMocks(this.$vm)
if (__PLATFORM__ === 'mp-baidu') {
const baiduComponentInstances = this.pageinstance.$baiduComponentInstances
// 初始化渲染数据
this.$vm.$mount()
},
ready () {
this.triggerEvent('__l', this.$vm)
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
}
} else {
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)
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()
this.$vm._isMounted = true
this.$vm.__call_hook('mounted')
this.$vm.__call_hook('onReady')
},
detached () {
if (__PLATFORM__ === 'mp-baidu') {
delete this.pageinstance.$baiduComponentInstances[this.id]
}
this.$vm.$destroy()
}
},
pageLifetimes: {
show (args) {
......@@ -84,7 +104,5 @@ export function createComponent (vueOptions) {
}
}
initMethods(componentOptions.methods, vueOptions)
return Component(componentOptions)
}
......@@ -2,12 +2,10 @@ import Vue from 'vue'
import {
getData,
initRefs,
initHooks,
initMocks,
initMethods,
handleLink,
handleEvent
handleEvent,
baiduPageDestroy
} from './util'
const hooks = [
......@@ -29,17 +27,16 @@ const hooks = [
export function createPage (vueOptions) {
vueOptions = vueOptions.default || vueOptions
const pageOptions = {
data: getData(vueOptions.data),
data: getData(vueOptions),
onLoad (args) {
this.$vm = new Vue(vueOptions)
this.$vm.mpType = 'page'
this.$vm.$mp = {
data: {},
page: this
if (__PLATFORM__ === 'mp-baidu') {
this.$baiduComponentInstances = Object.create(null)
}
initRefs(this.$vm)
initMocks(this.$vm)
this.$vm = new Vue(Object.assign(vueOptions, {
mpType: 'page',
mpInstance: this
}))
this.$vm.$mount()
this.$vm.__call_hook('onLoad', args)
......@@ -50,7 +47,11 @@ export function createPage (vueOptions) {
},
onUnload () {
this.$vm.__call_hook('onUnload')
this.$vm.$destroy()
if (__PLATFORM__ === 'mp-baidu') { // 百度组件不会在页面 unload 时触发 detached
baiduPageDestroy(this.$vm)
} else {
this.$vm.$destroy()
}
},
__e: handleEvent,
__l: handleLink
......@@ -58,7 +59,5 @@ export function createPage (vueOptions) {
initHooks(pageOptions, hooks)
initMethods(pageOptions, vueOptions)
return Page(pageOptions)
}
......@@ -16,39 +16,51 @@ export function initMocks (vm) {
})
}
export function initHooks (mpOptions, hooks) {
export function initHooks (mpOptions, hooks, delay = false) {
hooks.forEach(hook => {
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) {
// if (vueOptions.methods) {
// Object.assign(mpOptions, vueOptions.methods)
// }
}
export function getData (vueOptions) {
let data = vueOptions.data || {}
const methods = vueOptions.methods || {}
export function getData (data) {
if (typeof data === 'function') {
try {
return data()
data = data()
} 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]
function createObserver (name) {
return function observer (newVal, oldVal) {
if (this.$vm) {
this.$vm[name] = newVal // 为了触发其他非 render watcher
}
}
}
export function getProperties (props) {
const properties = {}
if (Array.isArray(props)) { // ['title']
props.forEach(key => {
properties[key] = null
properties[key] = {
type: null,
observer: createObserver(key)
}
})
} else if (isPlainObject(props)) { // {title:{type:String,default:''},content:String}
Object.keys(props).forEach(key => {
......@@ -60,10 +72,14 @@ export function getProperties (props) {
}
properties[key] = {
type: PROP_TYPES.includes(opts.type) ? opts.type : null,
value
value,
observer: createObserver(key)
}
} 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) {
event.target = event.target || {}
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 对象
event.mp = event
event.target = Object.assign({}, event.target, event.detail)
......@@ -124,6 +147,9 @@ export function handleEvent (event) {
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
......@@ -145,12 +171,12 @@ export function initRefs (vm) {
Object.defineProperty(vm, '$refs', {
get () {
const $refs = Object.create(null)
const components = mpInstance.selectAllComponents('.__ref__')
const components = mpInstance.selectAllComponents('.vue-ref')
components.forEach(component => {
const ref = component.dataset.ref
$refs[ref] = component.$vm
})
const forComponents = mpInstance.selectAllComponents('.__ref-in-for__')
const forComponents = mpInstance.selectAllComponents('.vue-ref-in-for')
forComponents.forEach(component => {
const ref = component.dataset.ref
if (!$refs[ref]) {
......@@ -161,4 +187,32 @@ export function initRefs (vm) {
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.
先完成此消息的编辑!
想要评论请 注册