refactor: beforeRender迁移到router创建后

This commit is contained in:
wanchun 2023-01-10 17:35:50 +08:00
parent 773082f8e1
commit cc20923e07
20 changed files with 87 additions and 272 deletions

View File

@ -1,12 +1,15 @@
import { winPath } from '@fesjs/utils'; import { readdirSync, statSync, existsSync } from 'fs';
import { readdirSync, statSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
import { winPath } from '@fesjs/utils';
/** /**
* 获取文件夹所有JS文件路径 * 获取文件夹所有JS文件路径
* @param {string} dir * @param {string} dir
*/ */
function getDirFilePaths(dir) { function getDirFilePaths(dir) {
if (!existsSync(dir)) {
return [];
}
const dirs = readdirSync(dir); const dirs = readdirSync(dir);
let pathList = []; let pathList = [];
for (const name of dirs) { for (const name of dirs) {
@ -26,13 +29,13 @@ function getDirFilePaths(dir) {
* @param {*} path * @param {*} path
*/ */
function pathToHump(path, root) { function pathToHump(path, root) {
return path.replace(root, '') return path
.replace(root, '')
.replace('.js', '') .replace('.js', '')
.replace(RegExp('(/|\\.|-|_)\\S', 'g'), text => text[1].toUpperCase()) .replace(RegExp('(/|\\.|-|_)\\S', 'g'), (text) => text[1].toUpperCase())
.replace(/\S/, text => text.toLowerCase()); .replace(/\S/, (text) => text.toLowerCase());
} }
function parsePlugin(paths = [], root) { function parsePlugin(paths = [], root) {
const plugins = []; const plugins = [];
const importPlugins = []; const importPlugins = [];
@ -53,6 +56,6 @@ export function parseStore(root) {
} }
}); });
return { return {
...parsePlugin(pluginPaths, root) ...parsePlugin(pluginPaths, root),
}; };
} }

View File

@ -1,8 +1,8 @@
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
import { winPath } from '@fesjs/utils'; import { winPath } from '@fesjs/utils';
import { parseStore } from './helper';
import { name } from '../package.json'; import { name } from '../package.json';
import { parseStore } from './helper';
const namespace = 'plugin-pinia'; const namespace = 'plugin-pinia';

View File

@ -16,8 +16,6 @@ export default function (api) {
key: 'addRuntimePluginKey', key: 'addRuntimePluginKey',
type: api.ApplyPluginsType.add, type: api.ApplyPluginsType.add,
initialValue: [ initialValue: [
// 初始化数据
'beforeRender',
// modify渲染工具 // modify渲染工具
'modifyClientRenderOpts', 'modifyClientRenderOpts',
'rootContainer', 'rootContainer',
@ -31,6 +29,8 @@ export default function (api) {
'modifyCreateHistory', 'modifyCreateHistory',
// 修改路由配置 // 修改路由配置
'modifyRoute', 'modifyRoute',
// 初始化数据
'beforeRender',
// 生成router时触发 // 生成router时触发
'onRouterCreated', 'onRouterCreated',
], ],

View File

@ -1,13 +1,11 @@
{{{ importsAhead }}} {{{ importsAhead }}}
import { import {
createApp, createApp,
reactive,
} from 'vue'; } from 'vue';
import { plugin } from './core/plugin'; import { plugin } from './core/plugin';
import './core/pluginRegister'; import './core/pluginRegister';
import { ApplyPluginsType } from '{{{ runtimePath }}}'; import { ApplyPluginsType } from '{{{ runtimePath }}}';
import { getRoutes } from './core/routes/routes'; import { getRoutes } from './core/routes/routes';
import { updateInitialState } from './initialState';
import DefaultContainer from './defaultContainer.jsx'; import DefaultContainer from './defaultContainer.jsx';
{{{ imports }}} {{{ imports }}}
@ -40,34 +38,10 @@ const renderClient = (opts = {}) => {
return app; return app;
} }
const beforeRender = async ({rootElement}) => {
const beforeRenderConfig = plugin.applyPlugins({
key: "beforeRender",
type: ApplyPluginsType.modify,
initialValue: {
loading: null,
action: null
},
});
if (typeof beforeRenderConfig.action === "function") {
const app = createApp(beforeRenderConfig.loading);
app.mount(rootElement);
try {
const initialState = await beforeRenderConfig.action();
updateInitialState(initialState || {})
} catch(e){
console.error(`[fes] beforeRender执行出现异常`);
console.error(e);
}
app.unmount();
app._container.innerHTML = '';
}
};
const getClientRender = (args = {}) => plugin.applyPlugins({ const getClientRender = (args = {}) => plugin.applyPlugins({
key: 'render', key: 'render',
type: ApplyPluginsType.compose, type: ApplyPluginsType.compose,
initialValue: async () => { initialValue: () => {
const opts = plugin.applyPlugins({ const opts = plugin.applyPlugins({
key: 'modifyClientRenderOpts', key: 'modifyClientRenderOpts',
type: ApplyPluginsType.modify, type: ApplyPluginsType.modify,
@ -80,7 +54,6 @@ const getClientRender = (args = {}) => plugin.applyPlugins({
{{/enableTitle}} {{/enableTitle}}
}, },
}); });
await beforeRender(opts);
return renderClient(opts); return renderClient(opts);
}, },
args, args,

View File

@ -1,5 +1,7 @@
import { createApp } from 'vue';
import { createRouter as createVueRouter, {{{ CREATE_HISTORY }}}, ApplyPluginsType } from '{{{ runtimePath }}}'; import { createRouter as createVueRouter, {{{ CREATE_HISTORY }}}, ApplyPluginsType } from '{{{ runtimePath }}}';
import { plugin } from '../plugin'; import { plugin } from '../plugin';
import { updateInitialState } from '../../initialState';
const ROUTER_BASE = '{{{ routerBase }}}'; const ROUTER_BASE = '{{{ routerBase }}}';
let router = null; let router = null;
@ -28,12 +30,45 @@ export const createRouter = (routes) => {
createHistory: createHistory createHistory: createHistory
}, },
}); });
history = route['createHistory']?.(route.base); history = route['createHistory']?.(route.base);
router = createVueRouter({ router = createVueRouter({
history, history,
routes: route.routes routes: route.routes
}); });
let isInit = false
router.beforeEach(async (to, from, next) => {
if(!isInit) {
isInit = true
const beforeRenderConfig = plugin.applyPlugins({
key: "beforeRender",
type: ApplyPluginsType.modify,
initialValue: {
loading: null,
action: null
},
});
if (typeof beforeRenderConfig.action === "function") {
const rootElement = document.createElement('div');
document.body.appendChild(rootElement)
const app = createApp(beforeRenderConfig.loading);
app.mount(rootElement);
try {
const initialState = await beforeRenderConfig.action({router, history});
updateInitialState(initialState || {})
} catch(e){
console.error(`[fes] beforeRender执行出现异常`);
console.error(e);
}
app.unmount();
app._container.innerHTML = '';
document.body.removeChild(rootElement);
}
}
next();
})
plugin.applyPlugins({ plugin.applyPlugins({
key: 'onRouterCreated', key: 'onRouterCreated',
type: ApplyPluginsType.event, type: ApplyPluginsType.event,

View File

@ -20,7 +20,7 @@ function isPromiseLike(obj) {
export const ApplyPluginsType = { export const ApplyPluginsType = {
compose: 'compose', compose: 'compose',
event: 'event', event: 'event',
modify: 'modify' modify: 'modify',
}; };
export default class Plugin { export default class Plugin {
@ -44,10 +44,7 @@ export default class Plugin {
assert(!!plugin.apply, 'register failed, plugin.apply must supplied'); assert(!!plugin.apply, 'register failed, plugin.apply must supplied');
assert(!!plugin.path, 'register failed, plugin.path must supplied'); assert(!!plugin.path, 'register failed, plugin.path must supplied');
Object.keys(plugin.apply).forEach((key) => { Object.keys(plugin.apply).forEach((key) => {
assert( assert(this.validKeys.indexOf(key) > -1, `register failed, invalid key ${key} from plugin ${plugin.path}.`);
this.validKeys.indexOf(key) > -1,
`register failed, invalid key ${key} from plugin ${plugin.path}.`
);
if (!this.hooks[key]) this.hooks[key] = []; if (!this.hooks[key]) this.hooks[key] = [];
this.hooks[key] = this.hooks[key].concat(plugin.apply[key]); this.hooks[key] = this.hooks[key].concat(plugin.apply[key]);
}); });
@ -74,20 +71,11 @@ export default class Plugin {
return hooks; return hooks;
} }
applyPlugins({ applyPlugins({ key, type, initialValue, args, async }) {
key,
type,
initialValue,
args,
async
}) {
const hooks = this.getHooks(key) || []; const hooks = this.getHooks(key) || [];
if (args) { if (args) {
assert( assert(typeof args === 'object', 'applyPlugins failed, args must be plain object.');
typeof args === 'object',
'applyPlugins failed, args must be plain object.'
);
} }
switch (type) { switch (type) {
@ -95,8 +83,10 @@ export default class Plugin {
if (async) { if (async) {
return hooks.reduce( return hooks.reduce(
async (memo, hook) => { async (memo, hook) => {
assert(typeof hook === 'function' || typeof hook === 'object' || isPromiseLike(hook), assert(
`applyPlugins failed, all hooks for key ${key} must be function, plain object or Promise.`); typeof hook === 'function' || typeof hook === 'object' || isPromiseLike(hook),
`applyPlugins failed, all hooks for key ${key} must be function, plain object or Promise.`,
);
if (isPromiseLike(memo)) { if (isPromiseLike(memo)) {
memo = await memo; memo = await memo;
} }
@ -112,15 +102,13 @@ export default class Plugin {
} }
return { ...memo, ...hook }; return { ...memo, ...hook };
}, },
isPromiseLike(initialValue) isPromiseLike(initialValue) ? initialValue : Promise.resolve(initialValue),
? initialValue
: Promise.resolve(initialValue)
); );
} }
return hooks.reduce((memo, hook) => { return hooks.reduce((memo, hook) => {
assert( assert(
typeof hook === 'function' || typeof hook === 'object', typeof hook === 'function' || typeof hook === 'object',
`applyPlugins failed, all hooks for key ${key} must be function or plain object.` `applyPlugins failed, all hooks for key ${key} must be function or plain object.`,
); );
if (typeof hook === 'function') { if (typeof hook === 'function') {
return hook(memo, args); return hook(memo, args);
@ -128,20 +116,17 @@ export default class Plugin {
return { ...memo, ...hook }; return { ...memo, ...hook };
}, initialValue); }, initialValue);
case ApplyPluginsType.event: case ApplyPluginsType.event:
return hooks.forEach((hook) => { return hooks.forEach((hook) => {
assert( assert(typeof hook === 'function', `applyPlugins failed, all hooks for key ${key} must be function.`);
typeof hook === 'function',
`applyPlugins failed, all hooks for key ${key} must be function.`
);
hook(args); hook(args);
}); });
case ApplyPluginsType.compose: case ApplyPluginsType.compose:
return () => _compose({ return () =>
_compose({
fns: hooks.concat(initialValue), fns: hooks.concat(initialValue),
args args,
})(); })();
default: default:
return null; return null;

View File

@ -36,9 +36,6 @@ export default defineBuildConfig({
icon: '/wine-outline.svg', icon: '/wine-outline.svg',
match: ['/route/*'], match: ['/route/*'],
}, },
{
name: 'store',
},
{ {
name: 'editor', name: 'editor',
icon: '/wine-outline.svg', icon: '/wine-outline.svg',
@ -74,9 +71,6 @@ export default defineBuildConfig({
['1', '有效的'], ['1', '有效的'],
], ],
}, },
vuex: {
strict: true,
},
dynamicImport: true, dynamicImport: true,
monacoEditor: { monacoEditor: {
languages: ['javascript', 'typescript', 'html', 'json'], languages: ['javascript', 'typescript', 'html', 'json'],

View File

@ -58,7 +58,6 @@
"@fesjs/plugin-pinia": "^3.0.0-rc.0", "@fesjs/plugin-pinia": "^3.0.0-rc.0",
"@fesjs/plugin-request": "^3.0.0-rc.0", "@fesjs/plugin-request": "^3.0.0-rc.0",
"@fesjs/plugin-sass": "^3.0.0-rc.0", "@fesjs/plugin-sass": "^3.0.0-rc.0",
"@fesjs/plugin-vuex": "^3.0.0-rc.0",
"@fesjs/plugin-windicss": "^3.0.0-rc.0", "@fesjs/plugin-windicss": "^3.0.0-rc.0",
"core-js": "^3.27.0", "core-js": "^3.27.0",
"cssnano": "^5.1.12", "cssnano": "^5.1.12",

View File

@ -1,4 +1,4 @@
import { access as accessApi, pinia } from '@fesjs/fes'; import { access as accessApi } from '@fesjs/fes';
import PageLoading from '@/components/pageLoading.vue'; import PageLoading from '@/components/pageLoading.vue';
import UserCenter from '@/components/userCenter.vue'; import UserCenter from '@/components/userCenter.vue';
import { useStore } from '@/store/main'; import { useStore } from '@/store/main';
@ -9,7 +9,7 @@ export const beforeRender = {
const { setRole } = accessApi; const { setRole } = accessApi;
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(() => { setTimeout(() => {
const store = useStore(pinia); const store = useStore();
store.$patch({ store.$patch({
userName: '李雷', userName: '李雷',
}); });

View File

@ -1,5 +1,5 @@
<template> <template>
<div>{{store.counter}}</div> <div>{{ store.counter }} {{ store.userName }}</div>
<FButton class="m-2" @click="store.increment">Button</FButton> <FButton class="m-2" @click="store.increment">Button</FButton>
</template> </template>
<config> <config>
@ -9,23 +9,21 @@
} }
</config> </config>
<script> <script>
import { useStore } from '@/store/main';
import { FButton } from '@fesjs/fes-design'; import { FButton } from '@fesjs/fes-design';
import { useStore } from '@/store/main';
export default { export default {
components: { components: {
FButton FButton,
}, },
setup() { setup() {
const store = useStore(); const store = useStore();
console.log(store); console.log(store);
return { return {
store store,
}; };
} },
}; };
</script> </script>
<style> <style></style>
</style>

View File

@ -1,60 +0,0 @@
<template>
<div class="page">
<h4>Vuex</h4>
<div>
<button @click="increment">click me{{doubleCount}}</button>
</div>
<div>
<button :disabled="disabled" @click="login">async login</button>
</div>
<div>
<button @click="fooBarIncrement">
foo/bar{{fooBarDoubleCount}}
</button>
</div>
<div>{{address}}</div>
</div>
</template>
<config>
{
"name": "store",
"title": "$store"
}
</config>
<script>
import { computed, ref } from 'vue';
import { useStore } from 'vuex';
import { MUTATION_TYPES, GETTER_TYPES, ACTION_TYPES } from '@fesjs/fes';
export default {
setup() {
const store = useStore();
console.log('store==>', store);
const disabled = ref(false);
return {
address: computed(() => store.getters[GETTER_TYPES.user.address]),
doubleCount: computed(
() => store.getters[GETTER_TYPES.counter.doubleCount]
),
disabled,
increment: () => store.commit(MUTATION_TYPES.counter.increment),
login: () => {
disabled.value = true;
store.dispatch(ACTION_TYPES.user.login).then((res) => {
// eslint-disable-next-line no-alert
window.alert(res);
disabled.value = false;
});
},
fooBarIncrement: () => store.commit(MUTATION_TYPES.fooBar.increment),
fooBarDoubleCount: computed(
() => store.getters[GETTER_TYPES.fooBar.doubleCount]
)
};
}
};
</script>
<style scoped>
.page {
}
</style>

View File

@ -7,8 +7,8 @@ export const useStore = defineStore('main', {
state: () => ({ state: () => ({
// all these properties will have their type inferred automatically // all these properties will have their type inferred automatically
counter: 0, counter: 0,
name: 'Eduardo', userName: 'Eduardo',
isAdmin: true isAdmin: true,
}), }),
actions: { actions: {
increment() { increment() {
@ -16,6 +16,6 @@ export const useStore = defineStore('main', {
}, },
randomizeCounter() { randomizeCounter() {
this.counter = Math.round(100 * Math.random()); this.counter = Math.round(100 * Math.random());
} },
} },
}); });

View File

@ -1,23 +0,0 @@
export default {
namespaced: true,
state: () => ({
count: 0
}),
mutations: {
increment(state) {
state.count++;
}
},
getters: {
doubleCount(state) {
return state.count * 2;
}
},
actions: {
asyncIncrement({ commit }) {
setTimeout(() => {
commit('increment');
}, 2000);
}
}
};

View File

@ -1,23 +0,0 @@
export default {
namespaced: true,
state: () => ({
count: 0
}),
mutations: {
increment(state) {
state.count++;
}
},
getters: {
doubleCount(state) {
return state.count * 2;
}
},
actions: {
asyncIncrement({ commit }) {
setTimeout(() => {
commit('increment');
}, 2000);
}
}
};

View File

@ -1,3 +0,0 @@
import { createLogger } from 'vuex';
export default createLogger();

View File

@ -1,54 +0,0 @@
export default {
namespaced: true,
state: () => ({
name: 'aring',
age: 20,
count: 0
}),
mutations: {
increment(state) {
state.count++;
}
},
getters: {
doubleCount(state) {
return state.count * 2;
}
},
actions: {
asyncIncrement({ commit }) {
setTimeout(() => {
commit('increment');
}, 2000);
},
login() {
return new Promise((reslove) => {
setTimeout(() => {
console.log('login');
reslove('OK');
}, 1000);
});
}
},
modules: {
address: {
state: () => ({
province: '广东省',
city: '深圳市',
zone: '南山区'
}),
getters: {
address(state) {
return state.province + state.city + state.zone;
}
}
},
posts: {
namespaced: true,
state: () => ({}),
mutations: {
doSomething() {}
}
}
}
};

View File

@ -84,9 +84,6 @@ export default defineBuildConfig({
['1', '有效的'], ['1', '有效的'],
], ],
}, },
vuex: {
strict: true,
},
dynamicImport: true, dynamicImport: true,
monacoEditor: { monacoEditor: {
languages: ['javascript', 'typescript', 'html', 'json'], languages: ['javascript', 'typescript', 'html', 'json'],

View File

@ -57,7 +57,6 @@
"@fesjs/plugin-sass": "^3.0.0-rc.0", "@fesjs/plugin-sass": "^3.0.0-rc.0",
"@fesjs/plugin-monaco-editor": "^3.0.0-rc.0", "@fesjs/plugin-monaco-editor": "^3.0.0-rc.0",
"@fesjs/plugin-windicss": "^3.0.0-rc.0", "@fesjs/plugin-windicss": "^3.0.0-rc.0",
"@fesjs/plugin-pinia": "^3.0.0-rc.0",
"@fesjs/plugin-watermark": "^3.0.0-rc.0", "@fesjs/plugin-watermark": "^3.0.0-rc.0",
"@fesjs/fes-design": "^0.7.0", "@fesjs/fes-design": "^0.7.0",
"core-js": "^3.27.0", "core-js": "^3.27.0",

View File

@ -1,8 +1,7 @@
import { access as accessApi, pinia, createWatermark } from '@fesjs/fes'; import { access as accessApi, createWatermark } from '@fesjs/fes';
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
import PageLoading from '@/components/pageLoading.vue'; import PageLoading from '@/components/pageLoading.vue';
import UserCenter from '@/components/userCenter.vue'; import UserCenter from '@/components/userCenter.vue';
import { useStore } from '@/store/main';
export const beforeRender = { export const beforeRender = {
loading: <PageLoading />, loading: <PageLoading />,
@ -10,11 +9,7 @@ export const beforeRender = {
const { setRole } = accessApi; const { setRole } = accessApi;
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(() => { setTimeout(() => {
const store = useStore(pinia); setRole('admin');
store.$patch({
userName: '李雷',
});
setRole('menuTest');
resolve({ resolve({
userName: '李雷', userName: '李雷',
}); });
@ -24,11 +19,11 @@ export const beforeRender = {
}, },
}; };
export const login = { // export const login = {
hasLogin() { // hasLogin() {
return !!sessionStorage.getItem('login'); // return !!sessionStorage.getItem('login');
}, // },
}; // };
export const layout = (layoutConfig, { initialState }) => ({ export const layout = (layoutConfig, { initialState }) => ({
...layoutConfig, ...layoutConfig,

View File

@ -8,7 +8,7 @@ export const useStore = defineStore('main', {
// all these properties will have their type inferred automatically // all these properties will have their type inferred automatically
counter: 0, counter: 0,
name: 'Eduardo', name: 'Eduardo',
isAdmin: true isAdmin: true,
}), }),
actions: { actions: {
increment() { increment() {
@ -16,6 +16,6 @@ export const useStore = defineStore('main', {
}, },
randomizeCounter() { randomizeCounter() {
this.counter = Math.round(100 * Math.random()); this.counter = Math.round(100 * Math.random());
} },
} },
}); });