@hippy/vue-router
Version:
Official router for hippy-vue
143 lines (128 loc) • 4.08 kB
JavaScript
/*
* Tencent is pleased to support the open source community by making
* Hippy available.
*
* Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable no-underscore-dangle */
/* eslint-disable no-param-reassign */
import { warn } from '../util/warn';
function resolveProps(route, config) {
switch (typeof config) {
case 'undefined':
return null;
case 'object':
return config;
case 'function':
return config(route);
case 'boolean':
return config ? route.params : undefined;
default:
if (process.env.NODE_ENV !== 'production') {
warn(
false,
`props in "${route.path}" is a ${typeof config}, `
+ 'expecting an object, function or boolean.',
);
}
return null;
}
}
export default {
name: 'RouterView',
functional: true,
props: {
name: {
type: String,
default: 'default',
},
},
render(_, {
props, children, parent, data,
}) {
// used by devtools to display a router-view badge
data.routerView = true;
// directly use parent context's createElement() function
// so that components rendered by router-view can resolve named slots
const h = parent.$createElement;
const { name } = props;
const route = parent.$route;
const cache = parent._routerViewCache || (parent._routerViewCache = {});
// determine current view depth, also check to see if the tree
// has been toggled inactive but kept-alive.
let depth = 0;
let inactive = false;
while (parent && parent._routerRoot !== parent) {
if (parent.$vnode && parent.$vnode.data.routerView) {
depth += 1;
}
if (parent._inactive) {
inactive = true;
}
parent = parent.$parent;
}
data.routerViewDepth = depth;
// render previous view if the tree is inactive and kept-alive
if (inactive) {
return h(cache[name], data, children);
}
const matched = route.matched[depth];
// render empty node if no matched route
if (!matched) {
cache[name] = null;
return h();
}
const component = matched.components[name];
cache[name] = component;
// attach instance registration hook
// this will be called in the instance's injected lifecycle hooks
data.registerRouteInstance = (vm, val) => {
// val could be undefined for unregistration
const current = matched.instances[name];
if (
(val && current !== vm)
|| (!val && current === vm)
) {
matched.instances[name] = val;
}
};
// also register instance in prepatch hook
// in case the same component instance is reused across different routes
if (!data.hook) {
data.hook = {};
}
data.hook.prepatch = (__, vnode) => {
matched.instances[name] = vnode.componentInstance;
};
// resolve props
let propsToPass = resolveProps(route, matched.props && matched.props[name]);
data.props = propsToPass;
if (propsToPass) {
// clone to prevent mutation
propsToPass = { ...propsToPass };
data.props = propsToPass;
// pass non-declared props as attrs
const attrs = data.attrs || {};
Object.keys(propsToPass).forEach((key) => {
if (!component.props || !(key in component.props)) {
attrs[key] = propsToPass[key];
delete propsToPass[key];
}
});
}
return h(component, data, children);
},
};