)} />
63 | }
64 |
65 | return fallback
66 | }
67 |
68 | Lazy.load = load
69 | Lazy.displayName = `Lazy`
70 |
71 | return Lazy
72 | }
73 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/hook/index.js:
--------------------------------------------------------------------------------
1 | export useCtrl from './useCtrl'
2 | export useModel from './useModel'
3 | export useModelActions from './useModelActions'
4 | export useModelState from './useModelState'
5 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/hook/useCtrl.js:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import GlobalContext from '../context'
3 |
4 | export default () => {
5 | let { ctrl } = useContext(GlobalContext)
6 | return ctrl
7 | }
8 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/hook/useModel.js:
--------------------------------------------------------------------------------
1 | import useModelState from './useModelState'
2 | import useModelActions from './useModelActions'
3 |
4 | export default () => {
5 | let state = useModelState()
6 | let actions = useModelActions()
7 | return [state, actions]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/hook/useModelActions.js:
--------------------------------------------------------------------------------
1 | import useCtrl from './useCtrl'
2 |
3 | export default () => {
4 | let ctrl = useCtrl()
5 | return ctrl.store.actions
6 | }
7 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/hook/useModelState.js:
--------------------------------------------------------------------------------
1 | import useCtrl from './useCtrl'
2 |
3 | export default () => {
4 | let ctrl = useCtrl()
5 | return ctrl.store.getState()
6 | }
7 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/index.js:
--------------------------------------------------------------------------------
1 | exports.start = require('./start')
2 | exports.build = require('./build')
3 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/middleware/cacheView.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 缓存视图-中间件
3 | */
4 | var createCache = require('./createCache')
5 |
6 | var defaults = {
7 | timeout: 5 * 60 * 1000, // 缓存时长
8 | max: 5, // 缓存数最大值
9 | headers: {
10 | // 缓存的 response headers 默认配置
11 | 'Content-Type': 'text/html',
12 | },
13 | key: (url, req) => url,
14 | debug: false,
15 | }
16 |
17 | var callNext = (req, res, next) => next()
18 |
19 | module.exports = function cacheView(settings) {
20 | // 只在生产环境,或者开启了 debug = true 的情况下,做缓存
21 | if (process.env.NODE_ENV !== 'production' && !(settings && settings.debug)) {
22 | return callNext
23 | }
24 | settings = Object.assign({}, defaults, settings)
25 |
26 | var cache = createCache()
27 |
28 | return function (req, res, next) {
29 | var cacheKey = settings.key(req.originalUrl, req)
30 | var cacheContent = cache.get(cacheKey)
31 |
32 | // 命中缓存,直接返回结果
33 | if (cacheContent) {
34 | res.set(settings.headers)
35 | res.send(cacheContent)
36 | return
37 | }
38 |
39 | // 劫持 res.render,缓存其结果
40 | res.sendResponse = res.send
41 | res.send = (body) => {
42 | res.sendResponse(body)
43 | cache.put(cacheKey, body, settings.timeout)
44 | if (cache.size() > settings.max) {
45 | // 如果缓存数大于最大值,删除第一个缓存
46 | cache.del(cache.keys()[0])
47 | }
48 | }
49 |
50 | // 调用下一个中间件
51 | next()
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/middleware/shareRoot.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 对 req.url 进行裁剪,以便适应不同的发布路径
3 | */
4 | module.exports = function shareRoot(...rootPathList) {
5 | let matcherList = rootPathList.map((rootPath) => {
6 | if (rootPath.charAt(rootPath.length - 1) === '/') {
7 | rootPath = rootPath.substr(0, rootPath.length - 1)
8 | }
9 |
10 | var ROOT_RE = new RegExp('^' + rootPath, 'i')
11 |
12 | return {
13 | rootPath,
14 | ROOT_RE,
15 | }
16 | })
17 |
18 | return function (req, res, next) {
19 | if (!req.basename) {
20 | req.basename = ''
21 | }
22 |
23 | for (let i = 0; i < matcherList.length; i++) {
24 | let { rootPath, ROOT_RE } = matcherList[i]
25 |
26 | if (ROOT_RE.test(req.url)) {
27 | req.url = req.url.replace(ROOT_RE, '')
28 | req.basename = rootPath
29 | if (req.url.charAt(0) !== '/') {
30 | req.url = '/' + req.url
31 | }
32 | break
33 | }
34 | }
35 |
36 | next()
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/mocha-runner.js:
--------------------------------------------------------------------------------
1 | process.env.BABEL_ENV = 'test'
2 | process.env.NODE_ENV = 'test'
3 |
4 | // Makes the script crash on unhandled rejections instead of silently
5 | // ignoring them. In the future, promise rejections that are not handled will
6 | // terminate the Node.js process with a non-zero exit code.
7 | process.on('unhandledRejection', (error) => {
8 | throw error
9 | })
10 |
11 | require('core-js/stable')
12 | require('regenerator-runtime/runtime')
13 |
14 | let options = require('yargs').argv
15 | let getConfig = require('./config')
16 | let config = getConfig(options)
17 |
18 | require('@babel/register')({
19 | ...config.babel(true, config),
20 | extensions: ['.es6', '.es', '.jsx', '.js', '.mjs', '.ts', '.tsx'],
21 | })
22 |
23 | let Mocha = require('mocha')
24 | let fs = require('fs')
25 | let path = require('path')
26 |
27 | // Instantiate a Mocha instance.
28 | let mocha = new Mocha({
29 | timeout: 20000,
30 | ...options,
31 | })
32 |
33 | function travelDirectoryToAddTestFiles(dir) {
34 | fs.readdirSync(dir).forEach((file) => {
35 | let filename = path.join(dir, file)
36 | // ignore node_modules
37 | if (filename.indexOf('node_modules') !== -1 || filename.indexOf('publish') !== -1) {
38 | return
39 | }
40 | // read file deeply
41 | if (fs.statSync(filename).isDirectory()) {
42 | return travelDirectoryToAddTestFiles(filename)
43 | }
44 | // add *test.js file to the mocha instance
45 | if (filename.substr(-8) === '-test.js') {
46 | return mocha.addFile(filename)
47 | }
48 | })
49 | }
50 |
51 | travelDirectoryToAddTestFiles(__dirname)
52 |
53 | // Run the tests.
54 | mocha.run(function (failures) {
55 | // exit with non-zero status if there were failures
56 | // Mocha Won't Force Exit
57 | process.exit(failures)
58 | })
59 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/nyc.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | include: ['project/**'],
3 | }
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/page/view.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Script from '../component/Script'
3 |
4 | export default function Page(props) {
5 | return (
6 |
7 |
8 |
9 |
13 |
14 |
15 |
16 | {props.title}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
32 |
33 |
34 |
35 |
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/polyfill/console.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 对console未定义及console方法未定义时,补充定义空对象、空函数,防止报错
3 | * 如IE 9在未开启过Dev Tools时,console对象将会是未定义
4 | */
5 | function consolePolyfill(window) {
6 | // Avoid `console` errors in browsers that lack a console.
7 | var method
8 | var noop = function () {}
9 | var methods = [
10 | 'assert',
11 | 'clear',
12 | 'count',
13 | 'debug',
14 | 'dir',
15 | 'dirxml',
16 | 'error',
17 | 'exception',
18 | 'group',
19 | 'groupCollapsed',
20 | 'groupEnd',
21 | 'info',
22 | 'log',
23 | 'markTimeline',
24 | 'profile',
25 | 'profileEnd',
26 | 'table',
27 | 'time',
28 | 'timeEnd',
29 | 'timeline',
30 | 'timelineEnd',
31 | 'timeStamp',
32 | 'trace',
33 | 'warn',
34 | 'msIsIndependentlyComposed',
35 | ]
36 | var length = methods.length
37 | var console = (window.console = window.console || {})
38 |
39 | while (length--) {
40 | method = methods[length]
41 |
42 | // Only stub undefined methods.
43 | if (!console[method]) {
44 | console[method] = noop
45 | }
46 | }
47 | }
48 |
49 | consolePolyfill(typeof window !== 'undefined' ? window : {})
50 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/polyfill/index.js:
--------------------------------------------------------------------------------
1 | require('./console')
2 | require('raf').polyfill(window)
3 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/build.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = 'production'
2 | const build = require('../build/babel')
3 | let PORT = 3333
4 | const ROOT = __dirname
5 | const config = {
6 | root: ROOT, // 项目根目录
7 | port: PORT, // server 端口号
8 | routes: 'routes', // 服务端路由目录
9 | layout: 'Layout', // 自定义 Layout
10 | staticPath: '/my_static', // 静态资源目录
11 | // devtool: 'source-map',
12 | // bundleAnalyzer: true,
13 | // staticEntry: 'index.html',
14 | publish: '../project_publish', // 打包输出目录
15 | gulp: {
16 | img: false,
17 | },
18 | useContentHash: true,
19 | useFileLoader: true,
20 | }
21 |
22 | async function main() {
23 | await build({ config })
24 | }
25 |
26 | main()
27 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "project",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "build.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "yj_gu",
11 | "license": "ISC"
12 | }
13 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/preview.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = 'production'
2 | const start = require('../start/babel')
3 | let PORT = 3333
4 | const ROOT = __dirname
5 | const config = {
6 | root: ROOT, // 项目根目录
7 | port: PORT, // server 端口号
8 | basename: ['/a', '/b'],
9 | routes: 'routes', // 服务端路由目录
10 | staticPath: '/my_static', // 静态资源目录
11 | publish: '../project_publish', // 打包输出目录
12 | layout: 'Layout', // 自定义 Layout
13 | // bundleAnalyzer: true,
14 | // useTypeCheck: true
15 | }
16 |
17 | async function main() {
18 | let { app, server } = await start({ config })
19 | console.log('started')
20 | }
21 |
22 | main()
23 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/routes/Layout.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Script from '../../component/Script'
3 |
4 | export default function Page(props) {
5 | return (
6 |
7 |
8 |
9 |
13 |
14 |
15 |
16 | {props.title}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
32 |
33 |
34 |
35 |
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/routes/index.js:
--------------------------------------------------------------------------------
1 | import my_router from './my_router'
2 |
3 | export { my_router }
4 |
5 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
6 |
7 | export const my_async_router = async (app, server) => {
8 | console.log('my_async_router is running')
9 | await delay(100)
10 | console.log('my_async_router is done')
11 | }
12 |
13 | export const my_another_async_router = async (app, server) => {
14 | console.log('my_another_async_router is running')
15 | await delay(100)
16 | console.log('my_another_async_router is done')
17 | }
18 |
19 | export const my_sync_router = [
20 | (app, server) => {
21 | console.log('my_sync_router is running')
22 | console.log('my_sync_router is done')
23 | },
24 | (app, server) => {
25 | console.log('my_sync_router1 is running')
26 | console.log('my_sync_router1 is done')
27 | },
28 | ]
29 |
30 | export default [
31 | async (app, server) => {
32 | console.log('index router is running')
33 | console.log('index router is done')
34 | },
35 | async (app, server) => {
36 | console.log('index1 router is running')
37 | console.log('index1 router is done')
38 | },
39 | ]
40 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/routes/my_router.js:
--------------------------------------------------------------------------------
1 | import { Router } from 'express'
2 |
3 | const router = Router()
4 |
5 | export default function (app, server) {
6 | app.use('/my_router', router)
7 | server.isTouched = true
8 | app.isTouched = true
9 | }
10 |
11 | router.get('/', (req, res) => {
12 | res.json({
13 | ok: true,
14 | })
15 | })
16 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/__tests__/a.test.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lucifier129/react-imvc/36a7fb0d77ae84275f5caa4f355fd3fbdb133823/packages/react-imvc-v2/project/src/__tests__/a.test.js
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/__tests__/a.test.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lucifier129/react-imvc/36a7fb0d77ae84275f5caa4f355fd3fbdb133823/packages/react-imvc-v2/project/src/__tests__/a.test.ts
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/basic-state/Controller.js:
--------------------------------------------------------------------------------
1 | import Controller from '../../../controller'
2 | import React from 'react'
3 |
4 | export default class extends Controller {
5 | View = View
6 | constructor(location, context) {
7 | super(location, context)
8 | if (context.isClient) {
9 | window.controller = this
10 | } else if (context.isServer) {
11 | global.controller = this
12 | }
13 | }
14 | }
15 |
16 | function View({ state }) {
17 | return {JSON.stringify(state, null, 2)}
18 | }
19 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/basic-state/__tests__/a.test.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lucifier129/react-imvc/36a7fb0d77ae84275f5caa4f355fd3fbdb133823/packages/react-imvc-v2/project/src/basic-state/__tests__/a.test.js
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/basic-state/__tests__/a.test.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lucifier129/react-imvc/36a7fb0d77ae84275f5caa4f355fd3fbdb133823/packages/react-imvc-v2/project/src/basic-state/__tests__/a.test.ts
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/error-boundary/Controller.js:
--------------------------------------------------------------------------------
1 | import Controller from '../../../controller'
2 | import ErrorBoundary from '../../../component/ErrorBoundary'
3 | import React, { useState, useEffect } from 'react'
4 |
5 | export default class extends Controller {
6 | SSR = true // enable server side rendering
7 | View = View
8 | actions = {
9 | TEST: () => {
10 | throw new Error('action-test')
11 | },
12 | }
13 | // componentWillCreate() {
14 | // throw new Error('componentWillCreate')
15 | // }
16 | getComponentFallback(displayName) {
17 | return `component-fallback: ${displayName}`
18 | }
19 | getViewFallback() {
20 | return 'view-fallback'
21 | }
22 | errorDidCatch(error, type) {
23 | console.log('error-did-catch', type, error)
24 | }
25 | componentDidMount() {
26 | setTimeout(() => {
27 | this.store.actions.TEST()
28 | })
29 | }
30 | }
31 |
32 | function View() {
33 | return (
34 | <>
35 |
36 |
37 |
38 | {() => {
39 | throw new Error('force')
40 | return
41 | }}
42 |
43 | >
44 | )
45 | }
46 |
47 | const Button = ({ message = 'test' }) => {
48 | let [count, setCount] = useState(0)
49 |
50 | if (count > 0) {
51 | throw new Error(message)
52 | }
53 |
54 | let handleClick = () => {
55 | setCount(count + 1)
56 | }
57 |
58 | return
59 | }
60 |
61 | const Section = ({}) =>
62 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/img/react.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lucifier129/react-imvc/36a7fb0d77ae84275f5caa4f355fd3fbdb133823/packages/react-imvc-v2/project/src/img/react.png
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/index.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | path: '/static_view',
4 | controller: () => import('./static-view/Controller'),
5 | },
6 | {
7 | path: '/static_view_csr',
8 | controller: () => import('./static-view-csr/Controller'),
9 | },
10 | {
11 | path: '/basic_state',
12 | controller: () => import('./basic-state/Controller'),
13 | },
14 | {
15 | path: '/error_boundary',
16 | controller: () => import('./error-boundary/Controller'),
17 | },
18 | {
19 | path: '/render_view',
20 | controller: () => import('./render-view/Controller'),
21 | },
22 | {
23 | path: '/life_cycle',
24 | controller: () => import('./life-cycle/Controller'),
25 | },
26 | {
27 | path: '/model',
28 | controller: () => import('./model/Controller'),
29 | },
30 | {
31 | path: '/lazy',
32 | controller: () => import('./lazy/controller'),
33 | },
34 | {
35 | path: '/preload',
36 | controller: () => import('./preload/Controller'),
37 | },
38 | {
39 | path: '/prefetch-header',
40 | controller: () => import('./prefetch-header/Controller'),
41 | },
42 | {
43 | path: '/batch-refresh',
44 | controller: () => import('./batch-refresh/Controller'),
45 | },
46 | ]
47 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/lazy/A.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export type CounterProps = {
4 | count: number
5 | }
6 |
7 | export const Counter = (props: CounterProps) => {
8 | const [count, setCount] = React.useState(props.count)
9 |
10 | return (
11 |
12 |
Counter A
13 |
14 |
15 | {count}
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | export default Counter
23 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/lazy/B.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export type CounterProps = {
4 | count: number
5 | }
6 |
7 | export const Counter = (props: CounterProps) => {
8 | const [count, setCount] = React.useState(props.count)
9 |
10 | return (
11 |
12 |
Counter B
13 |
14 |
15 | {count}
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | export default Counter
23 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/lazy/C.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export type CounterProps = {
4 | count: number
5 | }
6 |
7 | export const Counter = (props: CounterProps) => {
8 | const [count, setCount] = React.useState(props.count)
9 |
10 | return (
11 |
12 |
Counter C
13 |
14 |
15 | {count}
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | export default Counter
23 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/lazy/D.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export type CounterProps = {
4 | count: number
5 | }
6 |
7 | export const Counter = (props: CounterProps) => {
8 | const [count, setCount] = React.useState(props.count)
9 |
10 | return (
11 |
12 |
Counter D
13 |
14 |
15 | {count}
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | export default Counter
23 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/lazy/E.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export type CounterProps = {
4 | count: number
5 | }
6 |
7 | export const Counter = (props: CounterProps) => {
8 | const [count, setCount] = React.useState(props.count)
9 |
10 | return (
11 |
12 |
Counter E
13 |
14 |
15 | {count}
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | export default Counter
23 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/lazy/controller.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Controller from '../../../controller'
3 | import { lazy } from '../../../hoc/lazy'
4 |
5 | const components = {
6 | A: lazy(() => import('./A')),
7 | B: lazy(() => import('./B')),
8 | C: lazy(() => import('./C')),
9 | D: lazy(() => import('./D')),
10 | E: lazy(() => import('./E')),
11 | }
12 |
13 | export default class extends Controller {
14 | View = View
15 | async loadComponents() {
16 | await Promise.all([components.A.load(), components.B.load(), components.C.load()])
17 | }
18 | async componentWillCreate() {
19 | await this.loadComponents()
20 | }
21 | async viewWillHydrate() {
22 | await this.loadComponents()
23 | }
24 | }
25 |
26 | type ViewProps = {}
27 |
28 | function View({}: ViewProps) {
29 | return (
30 |
31 |
39 |
47 |
48 |
49 |
50 | fallback for d
} />
51 | fallback for e} />
52 |
53 | )
54 | }
55 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/life-cycle/Controller.js:
--------------------------------------------------------------------------------
1 | import Controller from '../../../controller'
2 | import React from 'react'
3 |
4 | export default class extends Controller {
5 | SSR = this.location.query.ssr !== '0'
6 | View = View
7 | componentDidMount() {
8 | console.log('didMount')
9 | }
10 | }
11 |
12 | function View({ state }) {
13 | console.log('Render View')
14 | return
15 | }
16 |
17 | function Child() {
18 | console.log('Render Child')
19 | return 'test'
20 | }
21 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/model/Controller.js:
--------------------------------------------------------------------------------
1 | import Controller from '../../../controller'
2 | import React from 'react'
3 |
4 | export default class extends Controller {
5 | SSR = this.location.query.ssr !== '0'
6 | View = View
7 | initialState = {
8 | count: 0,
9 | }
10 | actions = {
11 | incre: (state, step = 1) => {
12 | return {
13 | count: state.count + step,
14 | }
15 | },
16 | decre: (state, step = 1) => {
17 | return {
18 | count: state.count - step,
19 | }
20 | },
21 | }
22 | componentDidMount() {
23 | console.log('didMount')
24 | }
25 | }
26 |
27 | function View({ state, actions }) {
28 | return (
29 |
30 |
31 | {state.count}
32 |
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/prefetch-header/Controller.tsx:
--------------------------------------------------------------------------------
1 | import Controller from '../../../controller'
2 | import React from 'react'
3 | import { Style } from '../../../component'
4 |
5 | const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
6 |
7 | export default class extends Controller {
8 | // SSR = false // enable server side rendering
9 | preload = {
10 | css: '/prefetch-header/preload.css',
11 | }
12 | View = View
13 | disableEarlyHints: boolean = false
14 | async componentWillCreate() {
15 | this.addEarlyHintsLinks([
16 | {
17 | uri: '/img/react.png',
18 | rel: 'preload',
19 | as: 'image',
20 | },
21 | {
22 | uri: '/prefetch-header/preload.css',
23 | rel: 'preload',
24 | as: 'style',
25 | },
26 | ])
27 |
28 | this.flushHeaders()
29 |
30 | await delay(500)
31 | }
32 | constructor(location: any, context: any) {
33 | super(location, context)
34 | }
35 | }
36 |
37 | function View() {
38 | return (
39 |
40 |
41 |
static view content
42 |
43 | )
44 | }
45 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/prefetch-header/preload.css:
--------------------------------------------------------------------------------
1 | .style {
2 | height: 50px
3 | }
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/preload/Controller.js:
--------------------------------------------------------------------------------
1 | import Controller from '../../../controller'
2 | import { Style } from '../../../component'
3 | import React from 'react'
4 | import style from './style.css'
5 |
6 | export default class extends Controller {
7 | SSR = this.location.query.ssr !== '0'
8 | View = View
9 | constructor(location, context) {
10 | super(location, context)
11 | if (context.isClient) {
12 | window.controller = this
13 | } else if (context.isServer) {
14 | global.controller = this
15 | }
16 | }
17 | preload = {
18 | style: style,
19 | }
20 | publicPathPlaceholder = '#public_path'
21 | }
22 |
23 | function View({ state }) {
24 | return (
25 | <>
26 |
27 | {JSON.stringify(state, null, 2)}
28 |
29 |
30 | >
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/preload/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #666;
3 | color:aliceblue
4 | }
5 |
6 |
7 | .logo {
8 | width: 267px;
9 | height: 189px;
10 | background-image: url(#public_path/img/react.png);
11 | }
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/render-view/Controller.js:
--------------------------------------------------------------------------------
1 | import Controller from '../../../controller'
2 | import React from 'react'
3 |
4 | const delay = (time) =>
5 | new Promise((resolve) => {
6 | setTimeout(resolve, time)
7 | })
8 |
9 | export default class extends Controller {
10 | SSR = false
11 | View = View
12 | constructor(location, context) {
13 | super(location, context)
14 | if (context.isClient) {
15 | window.controller = this
16 | } else if (context.isServer) {
17 | global.controller = this
18 | }
19 | }
20 | async componentWillCreate() {
21 | this.renderView(() => -1)
22 | await delay(1000)
23 | }
24 | componentDidMount() {
25 | let count = 0
26 | let View = () => count
27 | setInterval(() => {
28 | this.renderView(View)
29 | count += 1
30 | }, 1000)
31 | }
32 | }
33 |
34 | function View({ state }) {
35 | return {JSON.stringify(state, null, 2)}
36 | }
37 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/static-view-csr/Controller.js:
--------------------------------------------------------------------------------
1 | import Controller from '../../../controller'
2 | import React from 'react'
3 | export default class extends Controller {
4 | SSR = false // disable server side rendering
5 | View = View
6 | }
7 |
8 | function View() {
9 | return static view content by client side rendering
10 | }
11 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/src/static-view/Controller.js:
--------------------------------------------------------------------------------
1 | import Controller from '../../../controller'
2 | import React from 'react'
3 | export default class extends Controller {
4 | SSR = true // enable server side rendering
5 | View = View
6 | }
7 |
8 | function View() {
9 | return static view content
10 | }
11 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/start.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = 'development'
2 | const start = require('../start/babel')
3 | let PORT = 3333
4 | const ROOT = __dirname
5 | const config = {
6 | root: ROOT, // 项目根目录
7 | port: PORT, // server 端口号
8 | // basename: ['/a', '/b'],
9 | routes: 'routes', // 服务端路由目录
10 | layout: 'Layout', // 自定义 Layout
11 | staticPath: '/my_static', // 静态资源目录
12 | useFileLoader: true,
13 | publish: '../project_publish', // 打包输出目录
14 | // bundleAnalyzer: true,
15 | // useTypeCheck: true
16 | }
17 |
18 | async function main() {
19 | let { app, server } = await start({ config })
20 | console.log('started')
21 | }
22 |
23 | main()
24 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/project/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "outDir": "dist",
5 | "declaration": true,
6 | "sourceMap": false,
7 | "target": "es5",
8 | "module": "commonjs",
9 | "skipLibCheck": true,
10 | "allowSyntheticDefaultImports": true,
11 | "moduleResolution": "node",
12 | "allowJs": true,
13 | "checkJs": false,
14 | "noUnusedLocals": true,
15 | "strict": true,
16 | "noImplicitAny": true,
17 | "noImplicitReturns": true,
18 | "preserveConstEnums": true,
19 | "noImplicitThis": true,
20 | "resolveJsonModule": true,
21 | "esModuleInterop": true,
22 | "removeComments": false,
23 | "jsx": "react",
24 | "types": ["node"],
25 | "rootDir": "../../"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/scripts/build.js:
--------------------------------------------------------------------------------
1 | let options = require('yargs').argv
2 | require('../build')(options)
3 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/scripts/start.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | let options = require('yargs').argv
4 |
5 | if (process.env.NODE_ENV === 'development') {
6 | let getConfig = require('../config')
7 | let config = getConfig(options)
8 | require('@babel/register')({
9 | ...config.babel(true, config),
10 | extensions: ['.es6', '.es', '.jsx', '.js', '.mjs', '.ts', '.tsx'],
11 | })
12 | }
13 |
14 | require('../start/index')({
15 | ...options,
16 | fromScript: true,
17 | })
18 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/scripts/test.js:
--------------------------------------------------------------------------------
1 | process.env.BABEL_ENV = 'test'
2 | process.env.NODE_ENV = 'test'
3 |
4 | // Makes the script crash on unhandled rejections instead of silently
5 | // ignoring them. In the future, promise rejections that are not handled will
6 | // terminate the Node.js process with a non-zero exit code.
7 | process.on('unhandledRejection', (error) => {
8 | throw error
9 | })
10 |
11 | require('core-js/stable')
12 | require('regenerator-runtime/runtime')
13 |
14 | let options = require('yargs').argv
15 | let getConfig = require('../config')
16 | let config = getConfig(options)
17 | require('@babel/register')({
18 | ...config.babel(true, config),
19 | extensions: ['.es6', '.es', '.jsx', '.js', '.mjs', '.ts', '.tsx'],
20 | })
21 |
22 | let Mocha = require('mocha')
23 | let fs = require('fs')
24 | let path = require('path')
25 |
26 | // Instantiate a Mocha instance.
27 | let mocha = new Mocha(options)
28 |
29 | function travelDirectoryToAddTestFiles(dir) {
30 | fs.readdirSync(dir).forEach((file) => {
31 | let filename = path.join(dir, file)
32 | // ignore node_modules
33 | if (filename.indexOf('node_modules') !== -1) {
34 | return
35 | }
36 | // read file deeply
37 | if (fs.statSync(filename).isDirectory()) {
38 | return travelDirectoryToAddTestFiles(filename)
39 | }
40 | // add *test.js file to the mocha instance
41 | if (filename.substr(-8) === '-test.js') {
42 | return mocha.addFile(filename)
43 | }
44 | })
45 | }
46 |
47 | travelDirectoryToAddTestFiles(process.cwd())
48 |
49 | // Run the tests.
50 | mocha.run(function (failures) {
51 | process.on('exit', function () {
52 | process.exit(failures) // exit with non-zero status if there were failures
53 | })
54 | })
55 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/start/babel.js:
--------------------------------------------------------------------------------
1 | const babel = require('../config/babel')
2 | const defaultConfig = require('../config/config.defaults')
3 |
4 | require('@babel/register')({
5 | ...babel(true, defaultConfig),
6 | extensions: ['.es6', '.es', '.jsx', '.js', '.mjs', '.ts', '.tsx'],
7 | })
8 |
9 | module.exports = require('./index')
10 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "esModuleInterop": true,
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "noEmit": true,
16 | "jsx": "preserve"
17 | },
18 | "include": ["."]
19 | }
20 |
--------------------------------------------------------------------------------
/packages/react-imvc-v2/util/htmlescape.ts:
--------------------------------------------------------------------------------
1 | // This utility is based on https://github.com/zertosh/htmlescape
2 | // License: https://github.com/zertosh/htmlescape/blob/0527ca7156a524d256101bb310a9f970f63078ad/LICENSE
3 |
4 | const ESCAPE_LOOKUP: { [match: string]: string } = {
5 | '&': '\\u0026',
6 | '>': '\\u003e',
7 | '<': '\\u003c',
8 | '\u2028': '\\u2028',
9 | '\u2029': '\\u2029',
10 | }
11 |
12 | const ESCAPE_REGEX = /[&><\u2028\u2029]/g
13 |
14 | export function htmlEscapeJsonString(str: string): string {
15 | return str.replace(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match])
16 | }
17 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4 |
5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of the level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
6 |
7 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8 |
9 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10 |
11 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12 |
13 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
14 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/.github/CONTRIBUTE.md:
--------------------------------------------------------------------------------
1 | # React-IMVC Contributing Guide
2 |
3 | - [Code of Conduct](./CODE_OF_CONDUCT.md)
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/.github/workflows/nodejs.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: IMVC
5 |
6 | on:
7 | push:
8 | branches: [master]
9 | pull_request:
10 | branches: [master]
11 |
12 | jobs:
13 | build:
14 | runs-on: ${{ matrix.os }}
15 |
16 | strategy:
17 | matrix:
18 | node-version: [10.x, 12.x, 14.x, 16.x, 17.x]
19 | os: [ubuntu-latest, macos-latest, windows-latest]
20 |
21 | steps:
22 | - uses: actions/checkout@v2
23 | - name: Use Node.js ${{ matrix.node-version }}
24 | uses: actions/setup-node@v1
25 | with:
26 | node-version: ${{ matrix.node-version }}
27 | - run: yarn
28 | - run: yarn build
29 | - run: yarn test
30 | env:
31 | CI: true
32 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/.github/workflows/playwright.yml:
--------------------------------------------------------------------------------
1 | name: Playwright Tests
2 | on:
3 | push:
4 | branches: [main, master]
5 | pull_request:
6 | branches: [main, master]
7 | jobs:
8 | test:
9 | timeout-minutes: 60
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 | - uses: actions/setup-node@v2
14 | with:
15 | node-version: '14.x'
16 | - name: Install dependencies
17 | run: yarn
18 | - name: Install Playwright
19 | run: npx playwright install --with-deps
20 | - name: Run Playwright tests
21 | run: yarn playwright test
22 | - uses: actions/upload-artifact@v2
23 | if: always()
24 | with:
25 | name: playwright-report
26 | path: playwright-report/
27 | retention-days: 30
28 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 |
6 | # production
7 | publish
8 | .idea
9 |
10 | # misc
11 | .DS_Store
12 | npm-debug.log
13 | .vscode
14 | _site
15 | mock/test.json
16 |
17 | dist
18 | coverage
19 | .npmrc
20 | test-results/
21 | playwright-report/
22 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true,
4 | "printWidth": 80
5 | }
6 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 工业聚
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/bin/scripts.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | require('../dist/bin/scripts')
3 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/build/createGulpTask.d.ts:
--------------------------------------------------------------------------------
1 | import createGulpTask from '../dist/build/createGulpTask'
2 | export default createGulpTask
3 | export * from '../dist/build/createGulpTask'
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/build/createGulpTask.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../dist/build/createGulpTask')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/build/createWebpackConfig.d.ts:
--------------------------------------------------------------------------------
1 | import createWebpackConfig from '../dist/build/createWebpackConfig'
2 | export default createWebpackConfig
3 | export * from '../dist/build/createWebpackConfig'
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/build/createWebpackConfig.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../dist/build/createGulpTask')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/build/index.d.ts:
--------------------------------------------------------------------------------
1 | import build from '../dist/build'
2 | export default build
3 | export * from '../dist/build'
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/build/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../dist/build')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/build/setup-dev-env.d.ts:
--------------------------------------------------------------------------------
1 | import setupDevEnv from '../dist/build/setup-dev-env'
2 | export default setupDevEnv
3 | export * from '../dist/build/setup-dev-env'
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/build/setup-dev-env.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../dist/build/setup-dev-env')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/ControllerProxy/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../../dist/component/ControllerProxy'
2 | import ControllerProxy from '../../dist/component/ControllerProxy'
3 | export default ControllerProxy
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/ControllerProxy/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../../dist/component/ControllerProxy')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/ErrorBoundary/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../../dist/component/ErrorBoundary'
2 | import ErrorBoundary from '../../dist/component/ErrorBoundary'
3 | export default ErrorBoundary
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/ErrorBoundary/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../../dist/component/ErrorBoundary')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/EventWrapper/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../../dist/component/EventWrapper'
2 | import EventWrapper from '../../dist/component/EventWrapper'
3 | export default EventWrapper
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/EventWrapper/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../../dist/component/EventWrapper')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/Input/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../../dist/component/Input'
2 | import Input from '../../dist/component/Input'
3 | export default Input
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/Input/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../../dist/component/Input')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/Link/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../../dist/component/Link'
2 | import Link from '../../dist/component/Link'
3 | export default Link
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/Link/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../../dist/component/Link')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/NavLink/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../../dist/component/NavLink'
2 | import NavLink from '../../dist/component/NavLink'
3 | export default NavLink
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/NavLink/index.js:
--------------------------------------------------------------------------------
1 | module.export = require('../../dist/component/NavLink')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/OuterClickWrapper/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../../dist/component/OuterClickWrapper'
2 | import OuterClickWrapper from '../../dist/component/OuterClickWrapper'
3 | export default OuterClickWrapper
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/OuterClickWrapper/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../../dist/component/OuterClickWrapper')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/Prefetch/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../../dist/component/Prefetch'
2 | import Prefetch from '../../dist/component/Prefetch'
3 | export default Prefetch
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/Prefetch/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../../dist/component/Prefetch')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/Script/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../../dist/component/Script'
2 | import Script from '../../dist/component/Script'
3 | export default Script
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/Script/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../../dist/component/Script')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/Style/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../../dist/component/Style'
2 | import Style from '../../dist/component/Style'
3 | export default Style
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/Style/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../../dist/component/Style')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/ViewManager/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../../dist/component/ViewManager'
2 | import ViewManager from '../../dist/component/ViewManager'
3 | export default ViewManager
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/ViewManager/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../../dist/component/ViewManager')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../dist/component'
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/component/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../dist/component')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/config/babel.d.ts:
--------------------------------------------------------------------------------
1 | import babel from '../dist/config/babel'
2 | export default babel
3 | export * from '../dist/config/babel'
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/config/babel.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../dist/config/babel')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/config/config.defaults.d.ts:
--------------------------------------------------------------------------------
1 | import defaults from '../dist/config/config.defaults'
2 | export default defaults
3 | export * from '../dist/config/config.defaults'
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/config/config.defaults.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../dist/config/config.defaults')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/config/index.d.ts:
--------------------------------------------------------------------------------
1 | import defaults from '../dist/config'
2 | export default defaults
3 | export * from '../dist/config'
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/config/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../dist/config')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/context/index.d.ts:
--------------------------------------------------------------------------------
1 | import context from '../dist/context'
2 | export default context
3 | export * from '../dist/context'
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/context/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../dist/context')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/controller/actions.d.ts:
--------------------------------------------------------------------------------
1 | import actions from '../dist/controller/actions'
2 | export default actions
3 | export * from '../dist/controller/actions'
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/controller/actions.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../dist/controller/actions')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/controller/attachDevToolsIfPossible.d.ts:
--------------------------------------------------------------------------------
1 | import attachDevToolsIfPossible from '../dist/controller/attachDevToolsIfPossible'
2 | export default attachDevToolsIfPossible
3 | export * from '../dist/controller/attachDevToolsIfPossible'
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/controller/attachDevToolsIfPossible.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../dist/controller/attachDevToolsIfPossible')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/controller/index.d.ts:
--------------------------------------------------------------------------------
1 | import Controller from '../dist/controller'
2 | export default Controller
3 | export * from '../dist/controller'
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/controller/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../dist/controller')
2 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/doc/Config/README.md:
--------------------------------------------------------------------------------
1 | # 介绍
2 |
3 | 配置文件为当前项目提供各个开发、打包流程的文件和配置信息。在一个项目中一般需要两份配置,一份在 `imvc.config.js` 中,这一份配置适用于项目的开发、测试和构建流程;另一份在 `start.js` 中,用于构建之后,在生产环境下运行的入口文件。当然,因为有默认配置,所以这两份配置都是可选的。
4 |
5 | 接下来我们看看,这两分配置的使用方式。
6 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/doc/Config/usage.md:
--------------------------------------------------------------------------------
1 | # 设置方式
2 |
3 | 上面提到需要设置两份配置:一份在项目配置文件 `imvc.config.js` 中,另一份在 `start.js` 中。
4 |
5 | ## 配置文件
6 |
7 | 目前配置文件支持五种类型文件:JS、TS、JSX、TSX、JSON。在 JS、TS、JSX、TSX 文件中最终要以 CommonJS 或 ES6 的方式导出配置信息。推荐使用 TS 文件,一遍对配置信息进行类型约束。示例如下:
8 |
9 | ```ts
10 | import path from 'path'
11 | import { defineConfig } from 'react-imvc'
12 |
13 | let PORT = 33336
14 | const ROOT = __dirname
15 |
16 | const config = defineConfig({
17 | root: ROOT, // 项目根目录
18 | port: PORT, // server 端口号
19 | routes: 'routes', // 服务端路由目录
20 | layout: 'Layout', // 自定义 Layout
21 | staticEntry: 'index.html',
22 | publish: '../publish',
23 | output: {
24 | path: path.resolve(ROOT, '../publish/static'),
25 | },
26 | webpackDevMiddleware: true,
27 | })
28 |
29 | export default config
30 | ```
31 |
32 | 或者 JSON 文件:
33 |
34 | ```json
35 | {
36 | "root": "", // 项目根目录
37 | "port": "3000", // server 端口号
38 | "routes": "routes", // 服务端路由目录
39 | "layout": "Layout", // 自定义 Layout
40 | "staticEntry": "index.html",
41 | "publish": "../publish",
42 | "output": {
43 | "path": "../publish/static"
44 | },
45 | "webpackDevMiddleware": true
46 | }
47 | ```
48 |
49 | ## 启动文件
50 |
51 | 启动文件中需要使用 React-IMVC 中暴露的接口启动,使用方式如下:
52 |
53 | ```ts
54 | import { start, defineConfig } from 'react-imvc'
55 |
56 | const PORT: number = 3333
57 | const ROOT = __dirname
58 | const config = defineConfig({
59 | root: ROOT, // 项目根目录
60 | port: PORT, // server 端口号
61 | routes: 'routes', // 服务端路由目录
62 | layout: 'Layout', // 自定义 Layout
63 | })
64 |
65 | async function main() {
66 | let { app, server } = await start({ config })
67 | console.log('started')
68 | }
69 |
70 | main()
71 | ```
72 |
73 | > TIP
74 | >
75 | > 启动文件中也可以引用配置文件 `imvc.config.ts` 中的配置。
76 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/doc/Isomorphic/README.md:
--------------------------------------------------------------------------------
1 | # 介绍
2 |
3 | 之前我们提到,在 React-IMVC 中 MVC 都是同构的,可以在服务器端和浏览器端同时运行,下面我们会详细介绍这一部分的特性及功能。
4 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/doc/Isomorphic/api.md:
--------------------------------------------------------------------------------
1 | # 接口路由
2 |
3 | 除了 fetch 相关的功能,我们还提供了接口路径自定义的功能。
4 |
5 | ## 属性设置
6 |
7 | ### API
8 |
9 | 类型:`Record`
10 |
11 | 默认值:`{}`
12 |
13 | 当 `ctrl.API` 存在时,将影响 `ctrl.fetch|get|post` 的行为,见 [ctrl.fetch](./data-fetch.md#fetch)
14 |
15 | ### restapi
16 |
17 | 类型:`string`
18 |
19 | 默认值:空字符串
20 |
21 | 当 `ctrl.restapi` 存在时,用 `restapi` 覆盖全局配置的 `restapi`,作为 `fetch` 方法的前缀补全
22 |
23 | ## 内置方法
24 |
25 | ### prependBasename
26 |
27 | 类型:
28 |
29 | ```ts
30 | ;(pathname: string) => string
31 | ```
32 |
33 | `ctrl.prependBasename` 方法,在 url 不是绝对路径时,把全局 `config.basename` 拼接在 url 的前头。
34 |
35 | `url = config.basename + url`
36 |
37 | ### prependPublicPath
38 |
39 | 类型:
40 |
41 | ```ts
42 | ;(pathname: string) => string
43 | ```
44 |
45 | `ctrl.prependPublicPath` 方法,在 url 不是绝对路径时,把全局配置 `config.publicPath` 拼接在 url 的前头。
46 |
47 | `url = config.publicPath + url`
48 |
49 | ### prependRestapi
50 |
51 | 类型:
52 |
53 | ```ts
54 | ;(url: string) => string
55 | ```
56 |
57 | `ctrl.prependRestapi` 方法,在 url 不是绝对路径时,把全局配置 `config.restapi` 拼接在 url 的前头。
58 |
59 | `url = config.restapi + url`
60 |
61 | 如果 url 是以 `/mock/` 开头,将使用 `ctrl.prependBasename` 方法。
62 |
63 | 注:`ctrl.fetch` 方法内部对 url 的处理,即是用到了 `ctrl.prependRestapi` 方法。
64 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/doc/Isomorphic/bom.md:
--------------------------------------------------------------------------------
1 | # BOM
2 |
3 | 这一部分我们将介绍浏览器相关的属性和功能。
4 |
5 | ## Location
6 |
7 | 类型:`Location`([HistoryLocation](https://github.com/tqma113/create-app/blob/master/src/share/type.d.ts))
8 |
9 | 这是用于记录当前页面在浏览器中的信息的对象,类似于 [location](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/location)。
10 |
11 | 存在于 Controller 中 `ctrl.location`。
12 |
13 | ## History
14 |
15 | 类型:`HistoryWithBFOL`
16 |
17 | 用于管理浏览器会话就,详情请见 [create-history](https://github.com/tqma113/history)。
18 |
19 | 存在于 Controller 中 `ctrl.history`。
20 |
21 | ## reload
22 |
23 | `ctrl.reload` 方法可实现刷新当前页面的功能,相当于单页应用的 `window.location.reload()`,通常整个页面不会刷新,而是重新实例化了一份 Controller。
24 |
25 | ## redirect
26 |
27 | `ctrl.redirect` 方法可实现重定向功能。
28 |
29 | 如果 url 是绝对路径,直接使用 url
30 | 如果 url 不是绝对路径,对 url 调用 `ctrl.prependBasename` 补前缀
31 | 如果 `isRaw` 为 `true`,则不进行补前缀
32 | 注意
33 |
34 | 重定向功能不是修改 `location` 的唯一途径,只有在需要的时候使用,其它情况下,考虑用 `ctrl.history` 里的跳转方法。
35 | 在服务端调用 `this.redirect` 时,内部会通过 `throw` 中断执行,模拟浏览器跳转时的中断代码效果
36 | 如果在 `try-catch` 语句里使用 `this.redirect`,会有一个副作用,必须判断 `catch` 的是不是 `Error` 的实例
37 |
38 | ```ts
39 | try {
40 | // do something
41 | this.redirect(targetUrl)
42 | } catch (error) {
43 | if (error instanceof Error) {
44 | // catch error
45 | }
46 | }
47 | ```
48 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/doc/Isomorphic/cache.md:
--------------------------------------------------------------------------------
1 | # 缓存
2 |
3 | 为了提高页面加载速度和效率,我们也提供了一些缓存的功能。
4 |
5 | ## 属性设置
6 |
7 | ### KeepAlive
8 |
9 | 类型:`boolean`
10 |
11 | 默认值:`false`
12 |
13 | 当 `ctrl.KeepAlive = true` 时,开启缓存模式。默认为 `false|undefined`
14 |
15 | `KeepAlive` 会缓存 `view`,`ctrl` 及其 `store`。
16 |
17 | 当页面前进或后退时,不再实例化一个新的 `ctrl`,而是从缓存里取出上次的 `ctrl`,并展示它的 `view` (通过设置 `dispaly`)。并触发 `pageDidBack` 生命周期。
18 |
19 | ### KeepAliveOnPush
20 |
21 | 类型:`boolean`
22 |
23 | 默认值:`false`
24 |
25 | 当 `ctrl.KeepAliveOnPush = true` 时,当页面通过 `history.push` 到另一个页面时,缓存当前页面。当页面回退到上一个页面时,清除当前页面的缓存。
26 |
27 | 该属性可以实现只为下一个新页面的提供缓存功能。
28 |
29 | 注:浏览器把前进/后退都视为 POP 事件,因此 A 页面 `history.push` 到 B 页面,B 页面 `history.back` 回到 A 时为 POP,A 页面再 `history.forward` 到 B 页面,也是 POP。`KeepAliveOnPush` 无法处理该场景,只能支持一次性来回的场景。
30 |
31 | ## 内置函数
32 |
33 | ### saveToCache
34 |
35 | `ctrl.saveToCache` 方法只在客户端存在,用以手动将 `ctrl` 加入 KeepAlive 缓存里。
36 |
37 | ### removeFromCache
38 |
39 | `ctrl.removeFromCache` 方法只在客户端存在,用以手动将 `ctrl` 从 KeepAlive 缓存里清除。
40 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/doc/Isomorphic/context.md:
--------------------------------------------------------------------------------
1 | # Context
2 |
3 | 这是用于应用中用于记录当前页面信息的对象,贯穿页面的整个生命周期,它存放于 `ctrl.context`。
4 |
5 | ## isServer
6 |
7 | 类型:`boolean`
8 |
9 | 是否在服务器端。
10 |
11 | ## isClient
12 |
13 | 类型:`boolean`
14 |
15 | 是否在浏览器端。
16 |
17 | ## basename
18 |
19 | 类型:`string`
20 |
21 | 配置文件中设置的 `basename`。
22 |
23 | ## publicPath
24 |
25 | 配置文件中的 `publicPath`。
26 |
27 | ## restapi
28 |
29 | 类型:`string`
30 |
31 | Controller 中设置的 restapi 的接口路径。
32 |
33 | ## preload
34 |
35 | 类型:`Record`
36 |
37 | 预加载资源,以键值对的形式。
38 |
39 | ## prevLocation
40 |
41 | 类型:`HistoryLocation`([HistoryLocation](https://github.com/tqma113/create-app/blob/master/src/share/type.d.ts))
42 |
43 | 当前页面之前的 `Location`。
44 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/doc/Isomorphic/event-handle.md:
--------------------------------------------------------------------------------
1 | # 事件处理
2 |
3 | 在 React-IMVC 有两种定义时间处理函数的方式:在 Controller 中定义和如普通 React 普通应用一样,在 View 组件中定义。
4 |
5 | ## Controller 中定义
6 |
7 | 示例如下:
8 |
9 | ```ts
10 | export default class extends Controller {
11 | ...
12 |
13 | handleClick = () => {
14 | ...
15 | }
16 | }
17 | ```
18 |
19 | 使用方式如下:
20 |
21 | ```ts
22 | function View({ ctrl }) {
23 | return (
24 |
25 |
26 |
27 | )
28 | }
29 | ```
30 |
31 | ## View 组件中定义
32 |
33 | 示例如下:
34 |
35 | ```ts
36 | function View() {
37 | handleClick = () => {
38 | ...
39 | }
40 | return (
41 |
42 |
43 |
44 | )
45 | }
46 | ```
47 |
48 | ## 总结
49 |
50 | 两种方式各有利弊,在 Controller 中定义,所有组件都可以使用,并且在处理函数中可以通过 `this` 关键字调用和获取 Controller 中的数据和内置函数、自定义函数等,但必须要通过 Controller 实例 `ctrl` 才能使用,而在 View 组件中定义较为灵活,且不受 `ctrl` 限制,但在实现时,则无法直接获取 Controller 中的内容,使用范围也不确定,所以没有那种方式是推荐的,具体视情况而定。
51 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/doc/Isomorphic/hoc.md:
--------------------------------------------------------------------------------
1 | # 高阶组件
2 |
3 | React-IMVC 提供了高阶组件,可以便利地实现一些特殊需求。
4 |
5 | ## connect
6 |
7 | 类型:
8 |
9 | ```ts
10 | any>(selector?: S | undefined): With>
11 | ```
12 |
13 | `connect` 是一个高阶函数,第一次调用时接受 `selector` 函数作为参数,返回 `withData` 函数。
14 |
15 | `withData` 函数接受一个 React 组件作为参数,返回新的 React 组件。`withData` 会将 `selector` 函数返回的数据,作为 `props` 传入新的 React 组件。
16 |
17 | `selector({ state, ctrl, actions })` 函数将得到一个 `data` 参数,其中包含三个字段 `state`, `ctrl`, `acitons`,分别对应 ctrl 里的 global state, this 和 actions 对象。
18 |
19 | ```ts
20 | import React from 'react'
21 | import connect from 'react-imvc/hoc/connect'
22 |
23 | const withData = connect(({ state }) => {
24 | return {
25 | content: state.loadingText,
26 | }
27 | })
28 |
29 | export default withData(Loading)
30 |
31 | function Loading(props) {
32 | if (!props.content) {
33 | return null
34 | }
35 | return (
36 |
37 |
38 |
39 | {props.content}
40 |
41 |
42 | )
43 | }
44 | ```
45 |
46 | ### lazy(loader)
47 |
48 | 类似于 `React.lazy`,但不需要 `Suspense` 组件包裹,它暴露了 `load` 方法,调用时会加载目标组件,否则渲染 `props.fallback`,如果没有 fallback,则默认为 null。
49 |
50 | 任何时候调用 load,都会在加载组件后,更新所有当前活跃的相关 Lazy 组件。
51 |
52 | 配合 `ctrl.componentWillCreate` 和 `ctrl.viewWillHydrate`,可以实现支持 SSR 的异步组件。
53 |
54 | ```tsx
55 | import React from 'react'
56 | import { lazy } from 'react-imvc/hoc/lazy'
57 |
58 | const LazySidebar = lazy(() => import('./sidebar'))
59 |
60 | const View = () => {
61 | const handleClick = () => {
62 | // 加载完成后,自动刷新 LazySideBar
63 | LazySideBar.load()
64 | }
65 | return (
66 |
67 |
68 | 占位
} />
69 |
70 | )
71 | }
72 |
73 | class Async extends Controller {
74 | View = View
75 | async componentWillCreate() {
76 | // SSR 时提前加载 LazySideBar
77 | await LazySideBar.load()
78 | }
79 |
80 | async viewWillHydrate() {
81 | // hydrate 前,提前加载 LazySideBar
82 | await LazySideBar.load()
83 | }
84 | }
85 | ```
86 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/doc/Isomorphic/persistence.md:
--------------------------------------------------------------------------------
1 | # 持久化
2 |
3 | ## Cookie
4 |
5 | 我们内置了一些管理 Cookie 的方法。
6 |
7 | ### getCookie
8 |
9 | 类型:
10 |
11 | ```ts
12 | (key: string, value: string, options?: Cookie.CookieAttributes | undefined) => void
13 | ```
14 |
15 | `ctrl.getCookie` 用以获取 cookie 里跟 `key` 参数对应的 `value` 值。第三个参数 `options` 为对象,可查看 [使用文档](https://github.com/js-cookie/js-cookie#cookie-attributes)。
16 |
17 | ### setCookie
18 |
19 | 类型:
20 |
21 | ```ts
22 | (key: string, value: string, options?: Cookie.CookieAttributes | undefined) => void
23 | ```
24 |
25 | `ctrl.setCookie` 用以设置 cookie 里跟 `key` 参数对应的 `value` 值。第三个参数 `options` 为对象,可查看 [使用文档](https://github.com/js-cookie/js-cookie#cookie-attributes)。
26 |
27 | ### removeCookie
28 |
29 | 类型:
30 |
31 | ```ts
32 | (key: string, options?: Cookie.CookieAttributes | undefined) => void
33 | ```
34 |
35 | `ctrl.removeCookie` 用以删除 cookie 里跟 `key` 参数对应的 `value` 值。第三个参数 `options` 为对象,可查看 [使用文档](https://github.com/js-cookie/js-cookie#cookie-attributes)。
36 |
37 | ### cookie
38 |
39 | 类型:
40 |
41 | ```ts
42 | ;(
43 | key: string,
44 | value?: string | undefined,
45 | options?: Cookie.CookieAttributes | undefined
46 | ) => any
47 | ```
48 |
49 | `ctrl.cookie` 方法是上述 `getCookie`、`setCookie` 方法的封装。
50 |
51 | - 当只有一个 `key` 参数时,内部调用 `getCookie` 方法。
52 |
53 | - 当有两个或两个以上的参数时,内部调用 `setCookie` 方法。
54 |
55 | ## LocalStorage
56 |
57 | [LocalStorage](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/localStorage) 的管理则推荐使用 `localStorage.setItem`、`localStorage.getItem`、`localStorage.removeImte` 和 `localStorage.clear` 进行管理。
58 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/doc/Isomorphic/route.md:
--------------------------------------------------------------------------------
1 | # 路由
2 |
3 | React-IMVC 项目中的同构代码部分是以路由文件为入口,即默认 src 目录下的入口文件。并且采取配置路由的方式,格式如下:
4 |
5 | ```ts
6 | export default [
7 | {
8 | path: '/static_view',
9 | controller: () => import('./static-view/Controller'),
10 | },
11 | ]
12 | ```
13 |
14 | 为数组的形式,单个路由中 path 对应时页面地址匹配字符串,controller 则是对应页面的 Controller,采用异步引入的方式(用于 Webpack 进行 Code split)。其中我们对 path 的匹配工具为 [path-to-regexp](https://github.com/pillarjs/path-to-regexp),所以关于路由匹配的相关规则请参照 [path-to-regexp](https://github.com/pillarjs/path-to-regexp) 的相关规则。
15 |
16 | 由于这样的路由形式,所以页面之间的渲染和数据管理都是独立的。下面我们会详细介绍单个页面的开发模式。
17 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/doc/Isomorphic/sass.md:
--------------------------------------------------------------------------------
1 | # 使用 Sass
2 |
3 | Sass 是一种 CSS 预处理器,它提供了一些便利的功能,使得 CSS 更容易编写和维护。
4 |
5 | ## 工作原理
6 |
7 | 一旦启用 sass 支持,`imvc` 将收集 `controller.tsx` 中依赖的所有 `css` 文件(包括 sass 和 css 文件),编译后拼接到 `controller.css/scss/sass` 中,并通过 `controller.preload` 机制加载和渲染。
8 |
9 | 所以,sass 的收集是一个 `build-time` 的新增功能,不会增加 `runtime` 的复杂度。
10 |
11 | 注意:只有文件名为`controller|Controller`作为入口文件的`controller.css`文件才会触发收集。
12 |
13 | ## 配置
14 |
15 | 在 `imvc.config.js` 中配置 `useSass` 选项:`true` 表示启用 Sass,`false` 表示禁用 Sass。
16 |
17 | ## 第一步:新增 controller.scss 文件
18 |
19 | 新增一个 `controller.scss` 文件,放在 `controller.tsx` 同级目录下。
20 |
21 | ## 第二步:在 controller.tsx 中引入 controller.scss
22 |
23 | 此处同样使用 `import` 语法引入 `controller.scss` 文件,并在 `preload` 中预加载。
24 |
25 | ```tsx
26 | import React from 'react'
27 | import Controller from 'react-imvc/controller'
28 | import controllerStyle from './controller.scss'
29 |
30 | export default class extends Controller {
31 | View = View
32 | preload = {
33 | controllerStyle,
34 | }
35 | }
36 |
37 | // 当组件渲染时,Style 标签会将 preload 里的同名 css 内容,展示为 style 标签。
38 | function View() {
39 | return (
40 |
41 |
42 |
43 | )
44 | }
45 | ```
46 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/doc/Isomorphic/serverRenderer.md:
--------------------------------------------------------------------------------
1 | # 自定义服务端渲染器
2 |
3 | react-imvc 支持在 `imvc.config.js` 中配置 `serverRenderer` 字段,来自定义服务端渲染器。
4 |
5 | 以 `styled-component` 的 SSR 为例:
6 |
7 | ```js
8 | const { renderToNodeStream, renderToString } = require('react-dom/server')
9 | const { ServerStyleSheet } = require('styled-components')
10 |
11 | const env = process.env.NODE_ENV
12 |
13 | // via renderToString
14 | const serverRenderer = (view) => {
15 | const sheet = new ServerStyleSheet()
16 | const html = renderToString(sheet.collectStyles(view))
17 | const styleTags = sheet.getStyleTags() // or sheet.getStyleElement();
18 | return styleTags + html
19 | }
20 |
21 | // via renderToNodeStream
22 | const streamingServerRenderer = (view) => {
23 | const sheet = new ServerStyleSheet()
24 | const jsx = sheet.collectStyles(view)
25 | const stream = sheet.interleaveWithNodeStream(renderToNodeStream(jsx))
26 | return stream
27 | }
28 |
29 | module.exports = {
30 | context: {
31 | env,
32 | },
33 | serverRenderer: streamingServerRenderer, // or serverRenderer
34 | }
35 | ```
36 |
--------------------------------------------------------------------------------
/packages/react-imvc-v3/doc/Isomorphic/static-file.md:
--------------------------------------------------------------------------------
1 | # 静态资源
2 |
3 | 处理数据的获取,我们还提供了静态资源的获取接口。
4 |
5 | ## 静态资源路由
6 |
7 | ## preload
8 |
9 | 我们在 Controller 中 提供了 `preload` 字段,类型为:
10 |
11 | ```ts
12 | Record
13 | ```
14 |
15 | 该对象用来在页面显示前,预加载 css, json 等数据。使用方式为:
16 |
17 | ```ts
18 | export default class extends Controller {
19 | preload = {
20 | 'main': '/path/to/css'
21 | }
22 |
23 | ...
24 | }
25 | ```
26 |
27 | 然后结合 我们提供的 [Style 组件](./components.md#style) 来加载该资源:
28 |
29 | ```ts
30 | import { Style } from 'react-imvc/components'
31 | function View() {
32 |
33 |