zhangyuang / egg-react-ssr Goto Github PK
View Code? Open in Web Editor NEW最小而美的Egg + React + SSR 服务端渲染应用骨架,同时支持JS和TS
Home Page: http://doc.ssr-fc.com/
License: MIT License
最小而美的Egg + React + SSR 服务端渲染应用骨架,同时支持JS和TS
Home Page: http://doc.ssr-fc.com/
License: MIT License
想在 index page 里面嵌套子的 react router,发现不工作,代码如下:
<input />
<Link to="/home">home</Link>
<Link to="/about">about</Link>
<Route path="/home">
<HomeTab />
</Route>
<Route path="/about">
<AboutTab />
</Route>
</div>
然后尝试修改了 config.ssr.js,让多个路径匹配到相同组件,如下:
{
path: '/',
exact: true,
Component: () => (require('@/page/index').default), // 这里使用一个function包裹为了让它延迟require
controller: 'page',
handler: 'index'
},
{
path: '/home',
exact: true,
Component: () => (require('@/page/index').default), // 这里使用一个function包裹为了让它延迟require
controller: 'page',
handler: 'index'
},
{
path: '/about',
exact: true,
Component: () => (require('@/page/index').default), // 这里使用一个function包裹为了让它延迟require
controller: 'page',
handler: 'index'
}
这样路由就能工作了,不过这样,他们共有的父节点,在每次切换路由时会更新,比如在上面的 input 组件里输入内容,再切换子路由,input 的输入会被清空
现象:
目前injectScript引入script时未带url查询参数或更新文件名,造成发布后http强缓存。
<script src='/static/js/Page.chunk.js'></script>
建议:
支持类似webpack的[contenthash]
<script src='/static/js/Page.chunk.[contenthash].js'></script>
可能是我的打开方式不对,复现 demo 如下,麻烦帮忙看看~ 感谢🙏
https://github.com/JohannLai/ssr-with-ts/commit/00f32189dc3e4978cbb1950d0cf450606bda9825
还增加了这个 文件https://github.com/JohannLai/ssr-with-ts/blob/master/config/config.default.js
原意是想在 page 底下写路由文件,然后引用整理放到 config.ssr.js 中,使到页面和路由不分开
例如这样:
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { Carousel } from 'antd';
export default class extends Component {
state = {};
render() {
return (
<section>
test
</section>
);
}
}
还是说必须要使用 function 的写法?
with this commit feat: 重构yk-cli
这里我们使用yargs来作为脚手架的底层服务
我们的应用的代码风格无论是js或是ts皆采用standardjs规范
为了使我们的脚手架能够在windows平台和*nix平台上都能够稳定使用,这里我们使用了shelljs,该脚本使用原生的fs模块来实现一些跨平台命令例如cp mv rm等命令
为了防止代码中出现大量的if else 以及callback语句造成可读性低下,在这里我们将callback都用promise包一层。这里可以使用Node原生的util.promisify方法或者自己手动封装
这里我们默认只提供一种css预处理器使用,因为如果要提供多种预处理器会涉及到比较大量复杂的文件处理操作,一个是容易影响稳定性,另一个是容易导致执行效率不高且代码可读性差
现象:
egg-react-ssr/example/ssr-with-antd/项目中,
3.chunk.css
被覆盖了
初步分析:
// package.json
{
"build": "rimraf dist && ykcli build && npm run build:server",
}
同时调用了webpack.config.base.js先后写入css文件,造成覆盖
// build/webpack.config.base.js
new MiniCssExtractPlugin({
filename: 'static/css/[name].css',
chunkFilename: 'static/css/[name].chunk.css'
})
ssr-middleware
example
const conf = require('./config/config.ssr')
const ssr = require('egg-react-ssr').koa(conf);
const Koa = require('koa');
const app = new Koa();
// mount routes from config
app.use(ssr)
// ctx.ssrRender()
app.use(async ctx => {
ctx.ssrRender(...);
});
app.listen(3000);
如题
vue3宣布放弃class API转为function based api,并提出了类似react-hooks的方案。既然都用上了react-hooks,那我们就没必要再使用传统的redux来实现跨组件的数据通信了
目前getInitialProps只支持路由级别的大组件才会被调用,next.js也是这样。
如果可以支持定义每一个小组件也使用getInitialProps来定义获取数据的逻辑,我们就可以精细的控制一个组件的获取数据以及渲染的过程,到底要在服务端做还是在客户端做。可以根据实际需求,来让需要优先展示的模块放在服务端渲染,次级模块在客户端渲染。将性能做到极致
包含2部分
在控制台,可以快速查看
[1] start clientRender
[1]
[1] Options:
[1] --help Show help [boolean]
[1] --version, -v [boolean] [default: false]
[1]
[1] TypeError: Property key of ObjectProperty expected node to be of a type ["Identifier","StringLiteral","NumericLiteral"] but
instead got "BooleanLiteral"
[1] at validate (C:\admin\github.com.project\pangu.admin\node_modules\_@babel_types@7.5.5@@babel\types\lib\definitions\utils.js:131:13)
[1] at Object.validate (C:\admin\github.com.project\pangu.admin\node_modules\_@babel_types@7.5.5@@babel\types\lib\definitions\core.js:526:11)
[1] at validateField (C:\admin\github.com.project\pangu.admin\node_modules\_@babel_types@7.5.5@@babel\types\lib\validators\validate.js:22:9)
[1] at Object.validate (C:\admin\github.com.project\pangu.admin\node_modules\_@babel_types@7.5.5@@babel\types\lib\validators\validate.js:16:3)
[1] at NodePath._replaceWith (C:\admin\github.com.project\pangu.admin\node_modules\_@babel_traverse@7.5.5@@babel\traverse\lib\path\replacement.js:194:9)
[1] at NodePath.replaceWith (C:\admin\github.com.project\pangu.admin\node_modules\_@babel_traverse@7.5.5@@babel\traverse\lib\path\replacement.js:178:8)
[1] at replaceAndEvaluateNode (C:\admin\github.com.project\pangu.admin\node_modules\_babel-plugin-transform-define@1.3.1@babel-plugin-transform-define\lib\index.js:117:12)
[1] at processNode (C:\Lizhicheng\github.com.project\pangu.admin\node_modules\_babel-plugin-transform-define@1.3.1@babel-plugin-transform-define\lib\index.js:143:5)
[1] at PluginPass.Identifier (C:\admin\github.com.project\pangu.admin\node_modules\_babel-plugin-transform-define@1.3.1@babel-plugin-transform-define\lib\index.js:23:9)
[1] at newFn (C:\admin\github.com.project\pangu.admin\node_modules\_@babel_traverse@7.5.5@@babel\traverse\lib\visitors.js:193:21)
node:v10.15.3
win10: v1803
"dependencies": {
"egg": "^2.23.0",
"egg-proxy": "^1.1.0",
"egg-scripts": "^2.11.0",
"egg-static": "^2.2.0",
"koa-router": "^7.4.0",
"react": "16.9.0",
"react-dev-utils": "^9.0.3",
"react-dom": "16.9.0",
"react-router-dom": "^5.0.1",
"ykfe-utils": "^2.1.1"
},
"devDependencies": {
"@babel/core": "^7.5.5",
"@babel/plugin-transform-runtime": "^7.5.5",
"@babel/polyfill": "^7.4.4",
"@babel/preset-env": "^7.5.5",
"@babel/preset-react": "^7.0.0",
"@babel/register": "^7.5.5",
"@babel/runtime": "^7.5.5",
"babel-loader": "8.0.6",
"browserslist": "^4.7.0",
"caniuse-lite": "1.0.30000989",
"concurrently": "^4.1.2",
"cross-env": "^5.2.1",
"css-hot-loader": "^1.4.4",
"css-loader": "3.2.0",
"css-modules-require-hook": "^4.2.3",
"egg-bin": "^4.13.1",
"file-loader": "4.2.0",
"less": "^3.10.3",
"less-loader": "^5.0.0",
"memory-fs": "^0.4.1",
"mini-css-extract-plugin": "^0.8.0",
"nodemon": "^1.19.2",
"optimize-css-assets-webpack-plugin": "5.0.3",
"postcss-flexbugs-fixes": "4.1.0",
"postcss-loader": "3.0.0",
"postcss-preset-env": "^6.7.0",
"postcss-safe-parser": "4.0.1",
"rimraf": "^3.0.0",
"terser-webpack-plugin": "^2.0.0",
"url-loader": "2.1.0",
"webpack": "4.39.3",
"webpack-bundle-analyzer": "^3.4.1",
"webpack-cli": "^3.3.8",
"webpack-dev-server": "3.8.0",
"webpack-manifest-plugin": "^2.0.4",
"webpack-merge": "^4.2.2",
"webpack-node-externals": "^1.7.2",
"yk-cli": "^2.2.1"
}
$ npm install yk-cli -g
$ ykcli init <Your Project Name>
$ cd <Your Project Name>
$ npm i
$ npm start
$ open http://localhost:7001
经过上面步骤尝试 测试 项目,发现命令行报错。后升级依赖包
。问题仍然没解决。错误结果一样。
{
ykfe-util: "^1.1.0"
}
I do like this:
Page.getInitialProps = () => {
console.log('👽 Page.getInitialProps');
return api.products.list().then(data => ({
products: data,
}));
};
api.products.list is like this:
return new Promise((resolve, reject) => {
return fetch(
${APISERVERHOST}/api/${this.name}.json?page=${option.page}&per_page=${option.perPage}
)
.then(response => {
if (response.status >= 400) {
reject('Bad response from server');
}
return resolve(response.json());
})
.then(data => {
return data;
})
.catch(err => {
throw new Error(err);
});
});
and got warnning:
WARNING in ./node_modules/ykfe-utils/es/renderToStream.js 34:25-60
[1] Critical dependency: the request of a dependency is an expression
[1] @ ./node_modules/ykfe-utils/es/index.js
[1] @ ./web/entry.js
[1] Child html-webpack-plugin for "index.html":
如题。
我看仓库列表中有相应的项目,但是没有文档……难以入坑
如题. 有例子吗?
能否补充一下PWA的example
报错-Error: Invariant failed: You should not use outside a
目前本应用能够通过脚手架新建比较稳定的运行,目前该配置可以解决大部分项目的基本需求,但为了应付灵活多变的需求,我们还需要不断的完善本应用所支持的场景,目前初步列出以下待办事项
提供结合多种流行的数据管理方式的example
服务端渲染应用的首屏时间是一个很难通过浏览器提供的API来直观看出的数据,我们必须在应用中手动触发时间统计埋点,如何让开发者做到无感知的给应用组件中添加时间统计的埋点也是一项有价值的任务,以上一个页面的unload时间为基准,即performance.timing.navigationStart的时间,目前只支持页面级别的组件信息统计
将bundle能够根据路由来分割能够进一步的加快首屏domContentLoaded的时间,不过对首屏时间的影响不大,但loadable结合SSR应用定义的getInitialProps方法来作为数据获取的方法,并没有那么简单,需要修改react-loadable源码
我们目前的getInitalProps方法只支持在路由级别的"大"组件中调用生效,如果能够支持在每一个更小粒度的组件也是通过该方法去获取数据,那我们就可以更精细的控制一个组件的渲染逻辑,究竟要放在服务端还是客户端。将首屏性能做到极致
目前给的example是单页面应用,如果要做多页面应用实质上是多个entry分别打包为单独的bundle,但如何做到配置更加灵活,更加轻便是个不小的难题,以及需要讨论既然ssr已经解决了seo的问题,就解决了多页面应用的一个优势。ssr + 路由分割理论上已经足够好用了,而多页面应用的缺点却很多,所以得考虑当前应用究竟有没有必要做成多页面应用的必要。
$ tree .
.
├── README.md
├── bin
│ └── cli.js
├── config
│ ├── env.js
│ ├── jest
│ │ ├── cssTransform.js
│ │ └── fileTransform.js
│ ├── paths.js
│ ├── util.js
│ ├── webpack.config.base.js
│ ├── webpack.config.client.js
│ └── webpack.config.server.js
├── doc
├── example
│ ├── app
│ │ ├── controller
│ │ │ └── page.js
│ │ └── router.js
│ ├── app.js
│ ├── config
│ │ ├── config.daily.js
│ │ ├── config.default.js
│ │ ├── config.local.js
│ │ ├── config.prod.js
│ │ ├── plugin.js
│ │ └── plugin.local.js
│ ├── dist
│ │ ├── Page.server.js
│ │ └── static
│ │ └── css
│ │ └── Page.css
│ ├── package.json
│ └── web
│ ├── assets
│ │ └── common.less
│ ├── entry.js
│ ├── index.html
│ ├── layout
│ │ ├── index.js
│ │ └── index.less
│ └── page
│ ├── index
│ │ ├── index.js
│ │ └── index.less
│ └── news
│ ├── index.js
│ └── index.less
├── package.json
└── src
├── index.js
└── tpl
├── js
│ ├── entry.js
│ └── package.json
└── ts
├── entry.js
└── package.json
21 directories, 37 files
node 版本 v10.15.1
(node:92402) UnhandledPromiseRejectionWarning: undefined (node:92402) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) (node:92402) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
你好,看文档的时候有一个疑惑,为什么output的 filename
和 chunkFilename
只使用了 [name]
, 而没有用 [contenthash]
/ [chunkhash]
等。
打包出来的名称没有变化,会不会导致因为缓存而让用户无法得到最新的文件?如果是使用协商缓存,那好像强缓存会更好一点,hash值变了就重新获取,协商还要来回通信
所以这部分的考量是什么呢?
windows下 运行npm script NODE_ENV=XXX
会报错
现状:目前支持本地同时启动两个端口分别是服务端渲染和客户端渲染。但是客户端渲染应用还得手动build bundle扔到cdn发布,无法做到生产环境无缝切换两种渲染方式。当服务器压力过大时可以由服务端渲染模式切换为客户端渲染模式。
目的:希望支持生产环境两种渲染模式的无缝切换
无论是使用模版引擎还是用目前的锚点+replace方式来拼接成完整的html文档,实现起来都很别扭。需要自己来定义如何组装成完成的页面。
如果使用react jsx来做外层模版,则直接renderToStream(<layout><page /></layout>
)就可以直接得到完整的页面文档了。而且jsx的写法比起模版引擎的写法无疑要舒服很多。
并且这种做法的好处是可以用使用者自由决定页面应该加入哪些额外的标签,而不用通过约定配置项来决定。之前的做法是通过约定config,再将config的值插入到锚点中,这样的做法不够灵活
目前
baseHtml.replace('<!-- Start Server Render Head -->', config.head ? config.head.join('') : '')
const docArr = baseHtml.split('<!-- Start Server Render Document -->')
const beginDoc = docArr[0].trim().replace('\n', '')
const beginDocStream = stringToStream(beginDoc.replace('<!-- Start Injecting Style Flows Up and Down -->', `${config.injectCss(chunkName).join('')}`))
const initialData = !isCsr ? `<script>window.__USE_SSR__=true;window.__USESSR__=true;window.__INITIAL_DATA__ =${serialize(ctx.serverData || {})};</script>` : ''
const injectScript = config.injectScript ? config.injectScript(chunkName).join('') : config.injectSrcipt(chunkName).join('')
const endDoc = docArr[1].trim().replace('\n', '')
const endDocStream = stringToStream(endDoc.replace('<!-- Start InitialData Script -->', initialData).replace('<!-- Start Client Script -->', injectScript))
const streamArr = isCsr ? [beginDocStream, endDocStream] : [beginDocStream, stream, endDocStream]
return multiStream(streamArr)
改造后
if (__isBrowser__) {
return <div id='app' ><div className='normal'><h1 className='title'><Link to='/'>Egg + React + SSR</Link><div className='author'>by ykfe</div></h1>{props.children}</div></div>
} else {
const { injectCss, injectScript, chunkName } = props.layoutData.app.config
return (
<html lang='en'>
<head>
<meta charSet='utf-8' />
<meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no' />
<meta name='theme-color' content='#000000' />
<title>React App</title>
{
injectCss && injectCss(chunkName).map(item => <link rel='stylesheet' href={item} />)
}
</head>
<body>
<div id='app' ><div className='normal'><h1 className='title'><Link to='/'>Egg + React + SSR</Link><div className='author'>by ykfe</div></h1>{props.children}</div></div>
<div dangerouslySetInnerHTML={{
__html: injectScript && injectScript(chunkName).join('')
}} />
</body>
</html>
)
}
目前唯一的点是,如何使用react jsx组件来作为csr html-webpack-plugin的模版。目前的思路是在本地开发时,使用renderToString api来将组件编译成html字符串并且写入到本地index.html文件,给html-webpack-plugin用
这几天为了兼容serverless场景对renderToStream方法进行了一些改造,经过测试发现,renderToStream.js
期望的被调用环境是在服务端,但是在打包客户端资源时,虽然我们已经启用了tree shaking
, 并且打包的结果也并没有renderToStream
方法相关代码,但是打包分析的时候以及本地开发模式下,webpack仍然会去分析使用未import的代码,故导致报错
目前想的解决方式是只将客户端或者双端能够通用的文件在ykfe-utils中export出来,而renderToStream不export,改为
const renderToStream = require('ykfe-utils/lib/renderToStream')
上面这种方式来引入具体的文件
如果你有更好的解决方式,欢迎评论
目前配置项前后端都用到了config.default.js,这就会导致前端代码中会将config.default.js的内容打包进去,有可能会包含一些隐私配置,或者会将后端代码打进去。建议将ssr的相关通用配置统一放到单独的一个配置文件进行维护
报错如下:
(node:24001) UnhandledPromiseRejectionWarning: Error: ENOENT: no such file or directory, open '/data/workSpace/wecard-homepage-ssr/node_modules/yk-cli/dist/Layout.server.js'
[1] at Object.openSync (fs.js:443:3)
[1] at Object.readFileSync (fs.js:343:35)
[1] at Object.Module._extensions..js (internal/modules/cjs/loader.js:788:20)
[1] at Module.load (internal/modules/cjs/loader.js:653:32)
[1] at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
[1] at Function.Module._load (internal/modules/cjs/loader.js:585:3)
[1] at Module.require (internal/modules/cjs/loader.js:692:17)
[1] at require (internal/modules/cjs/helpers.js:25:18)
[1] at renderLayout (/data/workSpace/wecard-homepage-ssr/node_modules/yk-cli/lib/renderLayout.js:49:18)
[1] (node:24001) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 6)
// 发布之前将example打成一个压缩包发布到yk-cli的npm包中
prePublish: {
tar ../example/ssr-with-js ssr-with-js.tgz
}
init: {
// 在每次用脚手架新建项目时,解压tgz包为example
tar ssr-with-js.tgz ssr-with-js
}
缺陷:不能保证example为远程仓库最新的代码,一旦需要用到最新的,需要强制更新cli
脚手架下载代码后,缓存到本地,在每次init之前,比对远程仓库版本与本地目录版本号,如果不一致重新下载
优势:能够保证用到最新的代码
缺陷:需要写获取远程版本号以及比对版本号的逻辑,且example单独分仓库,整体性有所欠缺
初始化.git文件,并且通过git config,使得只clone example文件夹,再将ssr-with-js文件夹利用mv命令提取到当前执行cli命令的目录。至于.git文件夹是否删除皆可
优势:无需将example分仓库维护,实现简单
缺陷:需要考虑用户设备无git命令以及mv命令的情况
业界类似方案:create-react-app
现存问题及优化建议:
js-sass版本在init过程,一直在“下载模版中...”,其实中断构建过程,运行项目是可以的,有待优化;
为了提高脚手架使用体验,建议在init完成后,新增初始化项目运行命令指南,例如:
npm install npm run start
而且注意到js与ts版本开发环境启动命令尚不同,能否统一,或者直接在脚手架初始化时做出命令指南,就不用去阅读package.json中脚本配置。只是建议,考虑下
hi,你好,可能是我使用不当出现以下问题,麻烦指导一下,感谢~
操作系统:centos7
使用语言: typescript
package.json
"dependencies": {
"egg-scripts": "^2.10.0",
"midway": "^1.0.0",
"mobx": "^5.13.0",
"mobx-react": "^6.1.4",
"react": "^16.10.2",
"react-dom": "^16.10.2",
"react-router-dom": "^5.1.2",
"yk-cli": "^2.6.0",
"ykfe-utils": "^2.1.2"
},
复现步骤:
import { Provider, useStaticRendering } from 'mobx-react';
const serverRender = async (ctx: Context): Promise<JSX.Element> => {
useStaticRendering(true);
// 服务端渲染 根据ctx.path获取请求的具体组件,调用getInitialProps并渲染
const ActiveComponent = getComponent(routes, ctx.path)()
const Layout = ActiveComponent.Layout || defaultLayout
const serverData = ActiveComponent.getInitialProps ? await ActiveComponent.getInitialProps(ctx) : {}
ctx.serverData = serverData
return <Provider Index={{test: 111}} >
<StaticRouter location={ctx.req.url} context={serverData}>
<Layout layoutData={ctx}>
<ActiveComponent {...serverData} />
</Layout>
</StaticRouter>
</Provider>
}
错误日志:
019-11-22 10:39:02,015 ERROR 22235 [-/10.13.82.114/-/130ms GET /] nodejs.Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
[0] [0] 1. You might have mismatching versions of React and the renderer (such as React DOM)
[0] [0] 2. You might be breaking the Rules of Hooks
[0] [0] 3. You might have more than one copy of React in the same app
[0] [0] See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.
[0] [0] 1. You might have mismatching versions of React and the renderer (such as React DOM)
[0] [0] 2. You might be breaking the Rules of Hooks
[0] [0] 3. You might have more than one copy of React in the same app
[0] [0] See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.
[0] [0] at resolveDispatcher (webpack-internal:///./node_modules/react/cjs/react.development.js:1590:13)
[0] [0] at Object.useContext (webpack-internal:///./node_modules/react/cjs/react.development.js:1598:20)
[0] [0] at X (webpack-internal:///./node_modules/mobx-react/dist/mobx-react.module.js:26:5804)
[0] [0] at processChild (/data/workSpace/test-mobx-ssr/node_modules/react-dom/cjs/react-dom-server.node.development.js:3204:14)
[0] [0] at resolve (/data/workSpace/test-mobx-ssr/node_modules/react-dom/cjs/react-dom-server.node.development.js:3124:5)
[0] [0] at ReactDOMServerRenderer.render (/data/workSpace/test-mobx-ssr/node_modules/react-dom/cjs/react-dom-server.node.development.js:3598:22)
[0] [0] at ReactDOMServerRenderer.read (/data/workSpace/test-mobx-ssr/node_modules/react-dom/cjs/react-dom-server.node.development.js:3536:29)
[0] [0] at ReactMarkupReadableStream._read (/data/workSpace/test-mobx-ssr/node_modules/react-dom/cjs/react-dom-server.node.development.js:4298:38)
[0] [0] at ReactMarkupReadableStream.Readable.read (_stream_readable.js:470:10)
[0] [0] at resume_ (_stream_readable.js:949:12)
[0] [0] headerSent: true
[0] [0] pid: 22235
[0] [0] hostname: VM_9_43_centos
[0] [0]
目前存在以下功能需要完善,欢迎PR
当我修改mobx的数据时,页面会报错,Uncaught Error: MobX Provider: The set of provided stores has changed,需要手动刷新页面
1)参见 https://circleci.com/docs/2.0/hello-world-windows/
保证win下的兼容性
2)目前项目里是node8的镜像,其他版本的也加一下
暂定node8+。如果激进点node10+也没问题
在dva effect中使用routerRedux提供的push api,可以跳转页面,但无法触发页面的生命周期调用
store.dispatch(push('/'))
Page Controller renderToStream Error TypeError: config.injectScript is not a function
目前示例里的前端页面代码都是放在web/page
目录下的。通过config.ssr.js中的这块代码
Component: () => (require('@/page/index').default)
去加载。
但是现在有一种场景就是,这些前端代码都是单独部署的独立应用,是否可以提供一种机制去加载这些独立应用编译打包后的js,css文件 @zhangyuang
我本地yarn csr 开启客户端渲染
查看界面元素发现这些资源文件:
但是看代码逻辑是不包含这些资源文件的啊:
不知道是不是我哪里没理解清楚? @zhangyuang
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.