├── README.md ├── web ├── pages │ ├── page │ │ ├── index.less │ │ └── index.jsx │ └── page2 │ │ ├── index.less │ │ └── index.jsx ├── reducer │ ├── index.js │ └── counter.js ├── action │ └── count.js ├── router.js ├── store │ └── index.js ├── render │ ├── clientRouter.js │ └── serverRouter.js └── components │ └── layout │ └── index.jsx ├── .gitignore ├── app ├── router.js ├── controller │ └── home.js ├── view │ └── index.njk └── build │ ├── page2.js │ └── page1.js ├── .vscode └── launch.json ├── .babelrc ├── package.json ├── index.js └── webpack.config.js /README.md: -------------------------------------------------------------------------------- 1 | # koa-react-ssr-render 2 | 基于koa的react服务器渲染 3 | 4 | > npm i 安装依赖 5 | 6 | > npm run build 7 | 8 | > 新开终端 执行 npm start -------------------------------------------------------------------------------- /web/pages/page/index.less: -------------------------------------------------------------------------------- 1 | .title-name { 2 | font-size: 18px; 3 | color: #2F3872; 4 | letter-spacing: 0; 5 | text-align: center; 6 | font-weight: 500 7 | 8 | } -------------------------------------------------------------------------------- /web/pages/page2/index.less: -------------------------------------------------------------------------------- 1 | .title-name { 2 | font-size: 18px; 3 | color: #2F3872; 4 | letter-spacing: 0; 5 | text-align: center; 6 | font-weight: 500 7 | 8 | } -------------------------------------------------------------------------------- /web/reducer/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import * as page from './counter' 3 | 4 | const rootReducer = combineReducers({ 5 | ...page 6 | }) 7 | 8 | export default rootReducer -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # production 7 | /dist 8 | 9 | /public 10 | # misc 11 | .DS_Store 12 | npm-debug.log* 13 | package-lock.json -------------------------------------------------------------------------------- /app/router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {router 实例化对象} app 4 | */ 5 | 6 | const home = require('./controller/home'); 7 | 8 | module.exports = (router) => { 9 | router.get('/',home.renderHtml); 10 | router.get('/page2',home.renderHtml); 11 | router.get('/favicon.ico',home.favicon); 12 | router.get('/test',home.test); 13 | } 14 | -------------------------------------------------------------------------------- /web/action/count.js: -------------------------------------------------------------------------------- 1 | export const set = (value) => ({ 2 | type: 'SET_COUNTER' 3 | }); 4 | 5 | export const setData = (value) => ({ 6 | type: 'SET_VALUE', 7 | value: value 8 | }); 9 | 10 | export const computedAdd = () => (dispatch, getState) => { 11 | dispatch(set()); 12 | } 13 | 14 | 15 | export const setValue = (val) => (dispatch, getState) => { 16 | 17 | dispatch(setData(val)); 18 | } -------------------------------------------------------------------------------- /app/controller/home.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 渲染react页面 4 | */ 5 | 6 | exports.renderHtml = async (ctx) => { 7 | let initState = ctx.query.state ? JSON.parse(ctx.query.state) : null; 8 | ctx.renderServer("page1", {store: JSON.stringify(initState ? initState : { counter: 1 }) }); 9 | } 10 | exports.favicon = (ctx) => { 11 | ctx.body = 'xxx'; 12 | } 13 | 14 | exports.test = (ctx) => { 15 | ctx.body = { 16 | data: `测试数据` 17 | } 18 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "program": "${workspaceFolder}\\index.js" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | { 4 | "presets": ["@babel/preset-react", 5 | ["@babel/preset-env",{ 6 | "targets": { 7 | "browsers": [ 8 | "ie >= 9", 9 | "ff >= 30", 10 | "chrome >= 34", 11 | "safari >= 7", 12 | "opera >= 23", 13 | "bb >= 10" 14 | ] 15 | } 16 | }] 17 | ], 18 | "plugins": [ 19 | [ 20 | "import", 21 | { "libraryName": "antd", "style": true } 22 | ] 23 | ] 24 | } -------------------------------------------------------------------------------- /app/view/index.njk: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | koa-React服务器渲染 8 | {{ link | safe }} 9 | 10 | 11 | 12 |
13 | {{ html | safe }} 14 |
15 | 16 | 19 | {{ script | safe }} 20 | -------------------------------------------------------------------------------- /web/reducer/counter.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | export const counter = (state = 0, action) => { 5 | switch (action.type) { 6 | case 'SET_COUNTER': 7 | 8 | return state += 1; 9 | break; 10 | default: 11 | return state; 12 | break; 13 | } 14 | } 15 | 16 | export const value = (state = null, action) => { 17 | switch (action.type) { 18 | case 'SET_VALUE': 19 | return action.value; 20 | break; 21 | default: 22 | return state; 23 | break; 24 | } 25 | } -------------------------------------------------------------------------------- /web/router.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StaticRouter, Switch } from 'react-router' 3 | import { BrowserRouter, Route, Link } from "react-router-dom"; 4 | import Page1 from './pages/page/index.jsx'; 5 | import Page2 from './pages/page2/index.jsx'; 6 | import Layout from './components/layout'; 7 | 8 | function RenderRouter() { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | 16 | ) 17 | } 18 | 19 | export default RenderRouter; -------------------------------------------------------------------------------- /web/store/index.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux' 2 | import thunk from 'redux-thunk' 3 | import rootReducer from '../reducer' 4 | 5 | const configureStore = (initStore) => { 6 | const store = createStore( 7 | rootReducer, 8 | initStore, 9 | applyMiddleware(thunk) 10 | ) 11 | 12 | // if (module.hot) { 13 | // // Enable Webpack hot module replacement for reducers 14 | // module.hot.accept('../reducers', () => { 15 | // store.replaceReducer(rootReducer) 16 | // }) 17 | // } 18 | 19 | return store 20 | } 21 | 22 | export default configureStore -------------------------------------------------------------------------------- /web/render/clientRouter.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { BrowserRouter } from "react-router-dom"; 4 | import { Provider } from 'react-redux'; 5 | import Router from '../router'; 6 | import configStore from '../store'; 7 | 8 | let initStore = window.__INIT_STORE__; 9 | let store = configStore(initStore); 10 | 11 | function ClientRender() { 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | 19 | ) 20 | } 21 | 22 | ReactDOM.hydrate(, document.querySelector('#app')); -------------------------------------------------------------------------------- /web/render/serverRouter.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StaticRouter } from 'react-router' 3 | import { Provider } from 'react-redux' 4 | import CreateStore from '../store/index.js' 5 | import Router from '../router.js'; 6 | 7 | function ServerRender(req, initStore) { 8 | let store = CreateStore(JSON.parse(initStore.store)); 9 | 10 | return (props, context) => { 11 | return ( 12 | 13 | 14 | 15 | 16 | 17 | 18 | ) 19 | } 20 | 21 | } 22 | 23 | export default ServerRender; 24 | -------------------------------------------------------------------------------- /web/components/layout/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux' 3 | import { bindActionCreators } from 'redux' 4 | import * as CounterActions from 'action/count'; 5 | 6 | class Layout extends Component { 7 | constructor() { 8 | super() 9 | } 10 | 11 | render() { 12 | return ( 13 | 14 | 15 | 首页 16 | 次页 17 | {this.props.counter} 18 | { 19 | this.props.children 20 | } 21 | 22 | ) 23 | } 24 | } 25 | const mapStateToProps = (state) => ({ 26 | counter: state.counter, 27 | state: state 28 | }) 29 | 30 | const mapDispatchToProps = (dispatch) => { 31 | return bindActionCreators(CounterActions, dispatch) 32 | } 33 | 34 | export default connect(mapStateToProps, mapDispatchToProps)(Layout) -------------------------------------------------------------------------------- /web/pages/page/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { bindActionCreators } from 'redux' 3 | import { connect } from 'react-redux' 4 | import * as CounterActions from 'action/count'; 5 | import axios from 'axios'; 6 | import './index.less'; 7 | 8 | class Counter extends React.Component { 9 | constructor() { 10 | super(); 11 | this.state = { 12 | test: '我是测试' 13 | } 14 | } 15 | 16 | onClick() { 17 | this.props.computedAdd(); 18 | } 19 | 20 | componentWillMount() { 21 | axios('http://127.0.0.1:3000/test').then((json) => { 22 | return json.data; 23 | }).then((res) => { 24 | this.props.setValue(res.data); 25 | }) 26 | } 27 | 28 | render() { 29 | 30 | return
31 |

32 | {this.props.counter} 33 |

34 | 35 |

{this.state.test}

36 | 37 |
38 | } 39 | } 40 | const mapStateToProps = (state) => { 41 | return { 42 | counter: state.counter, 43 | value: state.value 44 | } 45 | } 46 | 47 | const mapDispatchToProps = (dispatch) => { 48 | return bindActionCreators(CounterActions, dispatch) 49 | } 50 | 51 | export default connect(mapStateToProps, mapDispatchToProps)(Counter) 52 | -------------------------------------------------------------------------------- /web/pages/page2/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.less'; 4 | 5 | function onClick() { 6 | console.log("我是页面2") 7 | } 8 | function Title(props) { 9 | return
10 | 专栏 4Ark 5小时前 程序员 分享一些好用的网站 73 5 专栏 saltfish666 5小时前 Vue.js Vue 源码中一些util函数 45 3 专栏 嘉乐MrMaple 1小时前 程序员年终总结 一个技术创业者的2018年度回顾和总结 | 掘金年度征文 4 3 小册修言 前端性能优化原理与实践 购买人数: 4886 特价: 19.9元 分享 专栏 極楽 1小时前 Node.js微信小程序 微信小程序websocket聊天室 4 专栏 call_me_R 3小时前 前端前端框架 【译】框架与库的差异 12 6 专栏 大翰仔仔 3小时前 面试 刷前端面经笔记(七) 5 专栏 小生方勤 4小时前 前端 【前端词典】继承(二) - “回”的八种写法 20 HollisChuang 6小时前 程序员编程语言 指引趋势与方向!2019开发者调查报告出炉 5 6 专栏 boomyao 3小时前 React.js React是如何区分class和function的? 9 专栏 wznonstop 3小时前 前端掘金翻译计划 [译] 2019 前端性能优化年度总结 — 第五部分 7 热 专栏 frontdog 17小时前 前端 web页面录屏实现 89 7 专栏 超人汪小建 6小时前 前端算法 基于桶的基数排序 12 专栏 神奇排骨 3小时前 前端 浅析前端的模块化 8 专栏 我是一个前端 3小时前 JavaScript Javascript 事件循环event loop 6 freefrontend 5小时前 CSS 14 CSS Button Click Effects 6 专栏 boomyao 22小时前 React.js 为什么我们要添加 super(props) ? 36 3 热 张熠 1天前 Vue.jsTypeScript 值得一看,Vue 作者尤雨溪的回答【TypeScript 不适合在 vue 业务开发中使用吗?】 45 9 专栏 南波 22小时前 JavaScript面试 JS专题之继承 29 1 专栏 MarcusY 1天前 CSS 【译】通过css,用一个div做一个芝士汉堡 51 14 简单卟容易 1天前 Vue.js vue修饰符--可能是东半球最详细的文档(滑稽) 114 4 专栏 sea_ljf 1天前 JavaScript 知多一点二进制中的负数 45 6 热 huangsw 1年前 前端 强烈推荐--基于 vue2.x table 组件 386 9 专栏 serialcoder 1天前 JavaScript函数式编程 再谈 JavaScript 函数式编程的适用性 45 2 专栏 南波 1天前 JavaScript面试 JS专题之垃圾回收 66 热 专栏 芬达Tz 2天前 CSS 如何使用css绘制钻石 66 5 专栏 caiyongji 8月前 面试Java 你的知识死角不能否定你的技术能力 38 10 专栏 掘墓人4449 1天前 微信 微信支付申请相关问 11 | 12 |
13 | } 14 | 15 | // if(process.env.WEBPACK_WEB) { 16 | // ReactDOM.hydrate(, document.querySelector('#app')); 17 | // } else { 18 | export default Title; 19 | // } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-ssr", 3 | "version": "0.0.1", 4 | "description": "react服务器渲染模板", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build": "cross-env NODE_ENV=production webpack --progress", 9 | "start": "node index.js" 10 | }, 11 | "keywords": [ 12 | "react", 13 | "ssr", 14 | "node", 15 | "javascript", 16 | "babel", 17 | "import", 18 | "server" 19 | ], 20 | "author": "ruanguangguang", 21 | "license": "MIT", 22 | "dependencies": { 23 | "@babel/core": "^7.2.2", 24 | "@babel/preset-env": "^7.3.1", 25 | "@babel/preset-react": "^7.0.0", 26 | "autoprefixer": "^9.4.7", 27 | "axios": "^0.18.0", 28 | "babel-cli": "^6.26.0", 29 | "babel-loader": "^8.0.5", 30 | "babel-plugin-import": "^1.11.0", 31 | "cross-env": "^5.2.0", 32 | "css-loader": "^2.1.0", 33 | "emotion": "^10.0.6", 34 | "emotion-server": "^10.0.6", 35 | "isomorphic-style-loader": "^4.0.0", 36 | "koa": "^2.7.0", 37 | "koa-nunjucks-2": "^3.0.2", 38 | "koa-router": "^7.4.0", 39 | "koa-static": "^5.0.0", 40 | "less": "^3.0.1", 41 | "less-loader": "^4.1.0", 42 | "mini-css-extract-plugin": "^0.4.3", 43 | "postcss-loader": "^3.0.0", 44 | "precss": "^4.0.0", 45 | "react": "^16.7.0", 46 | "react-dom": "^16.7.0", 47 | "react-redux": "^6.0.0", 48 | "react-router": "^4.3.1", 49 | "react-router-dom": "^4.3.1", 50 | "redux": "^4.0.1", 51 | "redux-thunk": "^2.3.0", 52 | "style-loader": "^0.23.1", 53 | "webpack": "^4.29.0", 54 | "webpack-cli": "^3.2.1", 55 | "webpack-dev-server": "^3.1.14", 56 | "webpack-manifest-plugin": "^2.0.4", 57 | "webpack-node-externals": "^1.7.2" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const Koa = require('koa'); 2 | const app = new Koa(); 3 | const path = require('path'); 4 | const koaNunjucks = require('koa-nunjucks-2'); 5 | const React = require('react'); 6 | const KoaRouter = require('koa-router'); 7 | const ReactDOMServer = require('react-dom/server'); 8 | const koaStatic = require('koa-static'); 9 | const router = new KoaRouter(); 10 | 11 | const routerManagement = require('./app/router'); 12 | const manifest = require('./public/manifest.json'); 13 | 14 | 15 | /** 16 | * 处理链接 17 | * @param {*要进行服务器渲染的文件名默认是build文件夹下的文件} fileName 18 | */ 19 | function handleLink(fileName, req, defineParams) { 20 | let obj = {}; 21 | fileName = fileName.indexOf('.') !== -1 ? fileName.split('.')[0] : fileName; 22 | 23 | try { 24 | obj.script = `<script src="${manifest[`${fileName}.js`]}"></script>`; 25 | } catch (error) { 26 | console.error(new Error(error)); 27 | } 28 | try { 29 | obj.link = `<link rel="stylesheet" href="${manifest[`${fileName}.css`]}"/>`; 30 | 31 | } catch (error) { 32 | console.error(new Error(error)); 33 | } 34 | //服务器渲染 35 | const dom = require(path.join(process.cwd(),`app/build/${fileName}.js`)).default; 36 | let element = React.createElement(dom(req, defineParams)); 37 | obj.html = ReactDOMServer.renderToString(element); 38 | 39 | return obj; 40 | } 41 | 42 | /** 43 | * 服务器渲染,渲染HTML,渲染模板 44 | * @param {*} ctx 45 | */ 46 | function renderServer(ctx, next) { 47 | return async (fileName, defineParams) => { 48 | 49 | let obj = handleLink(fileName, ctx.req, defineParams); 50 | // 处理自定义参数 51 | defineParams = String(defineParams) === "[object Object]" ? defineParams : {}; 52 | obj = Object.assign(obj, defineParams); 53 | await ctx.render('index', obj); 54 | } 55 | } 56 | /** 57 | * 模板渲染 58 | */ 59 | app.use(koaNunjucks({ 60 | ext: 'njk', 61 | path: path.join(__dirname, 'app/view'), 62 | nunjucksConfig: { 63 | trimBlocks: true 64 | } 65 | })); 66 | /** 67 | * 设置静态资源 68 | */ 69 | app.use(koaStatic(path.resolve(__dirname, './public'), { 70 | maxage: 0, //浏览器缓存max-age(以毫秒为单位) 71 | hidden: false, //允许传输隐藏文件 72 | index: 'index.html', // 默认文件名,默认为'index.html' 73 | defer: true, //如果为true,则使用后return next(),允许任何下游中间件首先响应。 74 | gzip: true, //当客户端支持gzip时,如果存在扩展名为.gz的请求文件,请尝试自动提供文件的gzip压缩版本。默认为true。 75 | })); 76 | 77 | 78 | 79 | /** 80 | * 渲染Html 81 | */ 82 | app.use(async (ctx, next) => { 83 | ctx.renderServer = renderServer(ctx); 84 | await next(); 85 | }); 86 | 87 | 88 | /** 89 | * 注册路由 90 | */ 91 | routerManagement(router); 92 | app.use(router.routes()).use(router.allowedMethods()); 93 | 94 | app.listen(3000, () => { 95 | console.log("服务器已启动,请访问http://127.0.0.1:3000") 96 | }); -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 2 | const path = require('path'); 3 | const MiniCssExtractPlugin = require("mini-css-extract-plugin") 4 | const nodeExternals = require('webpack-node-externals'); 5 | const ManifestPlugin = require('webpack-manifest-plugin'); 6 | 7 | const resolve = { 8 | alias: { 9 | action: path.join(__dirname, './web/action') 10 | }, 11 | extensions: ['.js', '.jsx'] 12 | } 13 | 14 | //服务端配置 15 | const serverConfig = { 16 | target: 'node', 17 | entry: { 18 | page1: './web/render/serverRouter.js', 19 | }, 20 | resolve, 21 | output: { 22 | filename: '[name].js', 23 | path: path.resolve(__dirname, './app/build'), 24 | libraryTarget: 'commonjs' 25 | }, 26 | mode: process.env.NODE_ENV, 27 | externals: [nodeExternals()], 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.(jsx|js)?$/, 32 | exclude: /node_modules/, 33 | use: { 34 | loader: 'babel-loader' 35 | } 36 | }, 37 | { 38 | test: /\.(css|less)$/, 39 | use: [ 40 | { 41 | loader: 'css-loader', 42 | options: { 43 | importLoaders: 1 44 | } 45 | }, 46 | { 47 | loader: 'less-loader', 48 | } 49 | ] 50 | } 51 | ] 52 | } 53 | }; 54 | //客户端 55 | const clientConfig = { 56 | entry: { 57 | page1: './web/render/clientRouter.js' 58 | }, 59 | output: { 60 | filename: '[name].js', 61 | path: path.resolve(__dirname, './public'), 62 | }, 63 | mode: process.env.NODE_ENV, 64 | resolve, 65 | module: { 66 | rules: [ 67 | { 68 | test: /\.(jsx|js)?$/, 69 | exclude: /node_modules/, 70 | use: { 71 | loader: 'babel-loader' 72 | } 73 | }, 74 | { 75 | test: /\.(css|less)$/, 76 | use: [ 77 | { 78 | loader: MiniCssExtractPlugin.loader, 79 | }, 80 | { 81 | loader: 'css-loader' 82 | }, 83 | { 84 | loader: 'postcss-loader', 85 | options: { 86 | plugins: [ 87 | require('precss'), 88 | require('autoprefixer') 89 | ], 90 | } 91 | }, 92 | { 93 | loader: 'less-loader', 94 | options: { 95 | javascriptEnabled: true, 96 | // modifyVars: theme //antd默认主题样式 97 | } 98 | } 99 | ], 100 | }, 101 | ] 102 | }, 103 | 104 | plugins: [ 105 | // 提取样式,生成单独文件 106 | new MiniCssExtractPlugin({ 107 | filename: `[name].css`, 108 | chunkFilename: `[name].chunk.css` 109 | }), 110 | new ManifestPlugin() 111 | ] 112 | 113 | }; 114 | 115 | module.exports = [serverConfig, clientConfig] -------------------------------------------------------------------------------- /app/build/page2.js: -------------------------------------------------------------------------------- 1 | !function(o,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e=t();for(var n in e)("object"==typeof exports?exports:o)[n]=e[n]}}(global,function(){return function(o){var t={};function e(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return o[n].call(r.exports,r,r.exports,e),r.l=!0,r.exports}return e.m=o,e.c=t,e.d=function(o,t,n){e.o(o,t)||Object.defineProperty(o,t,{enumerable:!0,get:n})},e.r=function(o){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(o,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(o,"__esModule",{value:!0})},e.t=function(o,t){if(1&t&&(o=e(o)),8&t)return o;if(4&t&&"object"==typeof o&&o&&o.__esModule)return o;var n=Object.create(null);if(e.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:o}),2&t&&"string"!=typeof o)for(var r in o)e.d(n,r,function(t){return o[t]}.bind(null,r));return n},e.n=function(o){var t=o&&o.__esModule?function(){return o.default}:function(){return o};return e.d(t,"a",t),t},e.o=function(o,t){return Object.prototype.hasOwnProperty.call(o,t)},e.p="",e(e.s=5)}([function(o,t){o.exports=require("react")},function(o,t){o.exports=require("react-dom")},function(o,t,e){"use strict";o.exports=function(o){var t=[];return t.toString=function(){return this.map(function(t){var e=function(o,t){var e=o[1]||"",n=o[3];if(!n)return e;if(t&&"function"==typeof btoa){var r=(u=n,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(u))))+" */"),i=n.sources.map(function(o){return"/*# sourceURL="+n.sourceRoot+o+" */"});return[e].concat(i).concat([r]).join("\n")}var u;return[e].join("\n")}(t,o);return t[2]?"@media "+t[2]+"{"+e+"}":e}).join("")},t.i=function(o,e){"string"==typeof o&&(o=[[null,o,""]]);for(var n={},r=0;r<this.length;r++){var i=this[r][0];null!=i&&(n[i]=!0)}for(r=0;r<o.length;r++){var u=o[r];null!=u[0]&&n[u[0]]||(e&&!u[2]?u[2]=e:e&&(u[2]="("+u[2]+") and ("+e+")"),t.push(u))}},t}},,,function(o,t,e){"use strict";e.r(t);var n=e(0),r=e.n(n);e(1),e(6);function i(){console.log("我是按钮")}t.default=function(o){return r.a.createElement("div",{className:"title-name"},"百度为您找到相关结果约18,600,000个 百度App 10亿红包人人有份 只在百度App 扫码下载 Adobe PhotoShop CS6中文破解版下载_Adobe PhotoShop..._华军软件园 下载地址 大小: 1093MB 更新时间: 2018-11-22 AdobePhotoShopCS6是一款公告强大的图片编辑软件。PhotoshopCS6比PhotoShopCS5加强3D图像编辑,采用新的暗色调用户界面... www.onlinedown.net/sof... - 百度快照 为您推荐:pscs6免费序列号pscs6完整破解版百度云2018序列号cs6永久免费pscs6破解版吧ps6下载pscs6免费下载Photoshop CS6下载 photoshop cs6_adobe photoshop cs6中文版下载【精简版】-下载之家",r.a.createElement("p",null,"3xiazaizhan - 百度快照 Adobe Photoshop CS6 官方免费下载 图片处理软件 官方Photoshop ... Adobe Photoshop CS6 软件具备最先进的图像处理技术、全新的创意选项和极快的性能。借助新增的“内容识别”功... https://www.adobe.com/cn/produ... - 百度快照 【photoshop cs6 破解】Adobe Photoshop CS6 -ZOL软件下载 2018年10月16日 - Photoshop CS6中文版是Adobe公司旗下最为出名的图像处理软件之一,Photoshop CS6简体中文版集图像扫描、编辑修改、图像制作、广告创意,图像输入与输出于一体的图形图像... xiazai.zol.com.... - 百度快照 photoshop cs6 官方中文正式原版下载-photoshop cs6 中文版下载13... 2018年10月8日 - photoshop cs6 中文版,虽然adobe官方已经放弃了CS6这个版本,但是国内还是好多朋友在使用,由于官方原版好多东西... 西西软件园 - 百度快照 Photoshop CS6_Photoshop CS6下载【官方|绿色|中文】-太平洋下载"),r.a.createElement("button",{onClick:i},"我是按钮"))}},function(o,t,e){(o.exports=e(2)(!1)).push([o.i,".title-name {\n font-size: 18px;\n color: #2F3872;\n letter-spacing: 0;\n text-align: center;\n font-weight: 500;\n}\n",""])}])}); -------------------------------------------------------------------------------- /app/build/page1.js: -------------------------------------------------------------------------------- 1 | !function(t,e){for(var n in e)t[n]=e[n]}(exports,function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=11)}([function(t,e){t.exports=require("react")},function(t,e){t.exports=require("redux")},function(t,e){t.exports=require("react-redux")},function(t,e){t.exports=require("react-router")},function(t,e){t.exports=require("react-router-dom")},function(t,e,n){"use strict";t.exports=function(t){var e=[];return e.toString=function(){return this.map(function(e){var n=function(t,e){var n=t[1]||"",r=t[3];if(!r)return n;if(e&&"function"==typeof btoa){var o=(c=r,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(c))))+" */"),u=r.sources.map(function(t){return"/*# sourceURL="+r.sourceRoot+t+" */"});return[n].concat(u).concat([o]).join("\n")}var c;return[n].join("\n")}(e,t);return e[2]?"@media "+e[2]+"{"+n+"}":n}).join("")},e.i=function(t,n){"string"==typeof t&&(t=[[null,t,""]]);for(var r={},o=0;o<this.length;o++){var u=this[o][0];null!=u&&(r[u]=!0)}for(o=0;o<t.length;o++){var c=t[o];null!=c[0]&&r[c[0]]||(n&&!c[2]?c[2]=n:n&&(c[2]="("+c[2]+") and ("+n+")"),e.push(c))}},e}},function(t,e){t.exports=require("redux-thunk")},function(t,e){t.exports=require("axios")},function(t,e,n){(t.exports=n(5)(!1)).push([t.i,".title-name {\n font-size: 18px;\n color: #2F3872;\n letter-spacing: 0;\n text-align: center;\n font-weight: 500;\n}\n",""])},function(t,e){t.exports=require("react-dom")},function(t,e,n){(t.exports=n(5)(!1)).push([t.i,".title-name {\n font-size: 18px;\n color: #2F3872;\n letter-spacing: 0;\n text-align: center;\n font-weight: 500;\n}\n",""])},function(t,e,n){"use strict";n.r(e);var r={};n.r(r),n.d(r,"counter",function(){return p}),n.d(r,"value",function(){return b});var o={};n.r(o),n.d(o,"set",function(){return h}),n.d(o,"setData",function(){return S}),n.d(o,"computedAdd",function(){return O}),n.d(o,"setValue",function(){return j});var u=n(0),c=n.n(u),i=n(3),a=n(2),f=n(1),l=n(6),s=n.n(l),p=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0;switch((arguments.length>1?arguments[1]:void 0).type){case"SET_COUNTER":return t+1;default:return t}},b=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,e=arguments.length>1?arguments[1]:void 0;switch(e.type){case"SET_VALUE":return e.value;default:return t}};function y(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}var v=Object(f.combineReducers)(function(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{},r=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(n).filter(function(t){return Object.getOwnPropertyDescriptor(n,t).enumerable}))),r.forEach(function(e){y(t,e,n[e])})}return t}({},r)),d=function(t){return Object(f.createStore)(v,t,Object(f.applyMiddleware)(s.a))},m=n(4),h=function(t){return{type:"SET_COUNTER"}},S=function(t){return{type:"SET_VALUE",value:t}},O=function(){return function(t,e){t(h())}},j=function(t){return function(e,n){e(S(t))}},g=n(7),E=n.n(g);n(8);function w(t){return(w="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function x(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}function _(t,e){return!e||"object"!==w(e)&&"function"!=typeof e?function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t):e}function P(t){return(P=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function C(t,e){return(C=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}var k=function(t){function e(){var t;return function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e),(t=_(this,P(e).call(this))).state={test:"我是测试"},t}var n,r,o;return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&C(t,e)}(e,c.a.Component),n=e,(r=[{key:"onClick",value:function(){this.props.computedAdd()}},{key:"componentWillMount",value:function(){var t=this;E()("http://127.0.0.1:3000/test").then(function(t){return t.data}).then(function(e){t.props.setValue(e.data)})}},{key:"render",value:function(){return c.a.createElement("div",{className:"title-name"},c.a.createElement("h1",null,this.props.counter),c.a.createElement("h2",null,this.state.test),c.a.createElement("button",{onClick:this.onClick.bind(this)},"点我加1"))}}])&&x(n.prototype,r),o&&x(n,o),e}(),R=Object(a.connect)(function(t){return{counter:t.counter,value:t.value}},function(t){return Object(f.bindActionCreators)(o,t)})(k);n(9),n(10);function T(){console.log("我是页面2")}var J=function(t){return c.a.createElement("div",{className:"title-name"},"专栏 4Ark 5小时前 程序员 分享一些好用的网站 73 5 专栏 saltfish666 5小时前 Vue.js Vue 源码中一些util函数 45 3 专栏 嘉乐MrMaple 1小时前 程序员年终总结 一个技术创业者的2018年度回顾和总结 | 掘金年度征文 4 3 小册修言 前端性能优化原理与实践 购买人数: 4886 特价: 19.9元 分享 专栏 極楽 1小时前 Node.js微信小程序 微信小程序websocket聊天室 4 专栏 call_me_R 3小时前 前端前端框架 【译】框架与库的差异 12 6 专栏 大翰仔仔 3小时前 面试 刷前端面经笔记(七) 5 专栏 小生方勤 4小时前 前端 【前端词典】继承(二) - “回”的八种写法 20 HollisChuang 6小时前 程序员编程语言 指引趋势与方向!2019开发者调查报告出炉 5 6 专栏 boomyao 3小时前 React.js React是如何区分class和function的? 9 专栏 wznonstop 3小时前 前端掘金翻译计划 [译] 2019 前端性能优化年度总结 — 第五部分 7 热 专栏 frontdog 17小时前 前端 web页面录屏实现 89 7 专栏 超人汪小建 6小时前 前端算法 基于桶的基数排序 12 专栏 神奇排骨 3小时前 前端 浅析前端的模块化 8 专栏 我是一个前端 3小时前 JavaScript Javascript 事件循环event loop 6 freefrontend 5小时前 CSS 14 CSS Button Click Effects 6 专栏 boomyao 22小时前 React.js 为什么我们要添加 super(props) ? 36 3 热 张熠 1天前 Vue.jsTypeScript 值得一看,Vue 作者尤雨溪的回答【TypeScript 不适合在 vue 业务开发中使用吗?】 45 9 专栏 南波 22小时前 JavaScript面试 JS专题之继承 29 1 专栏 MarcusY 1天前 CSS 【译】通过css,用一个div做一个芝士汉堡 51 14 简单卟容易 1天前 Vue.js vue修饰符--可能是东半球最详细的文档(滑稽) 114 4 专栏 sea_ljf 1天前 JavaScript 知多一点二进制中的负数 45 6 热 huangsw 1年前 前端 强烈推荐--基于 vue2.x table 组件 386 9 专栏 serialcoder 1天前 JavaScript函数式编程 再谈 JavaScript 函数式编程的适用性 45 2 专栏 南波 1天前 JavaScript面试 JS专题之垃圾回收 66 热 专栏 芬达Tz 2天前 CSS 如何使用css绘制钻石 66 5 专栏 caiyongji 8月前 面试Java 你的知识死角不能否定你的技术能力 38 10 专栏 掘墓人4449 1天前 微信 微信支付申请相关问",c.a.createElement("button",{onClick:T},"我是按钮"))};function M(t){return(M="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function V(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}function q(t,e){return!e||"object"!==M(e)&&"function"!=typeof e?function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t):e}function A(t){return(A=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function N(t,e){return(N=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}var U=function(t){function e(){return function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e),q(this,A(e).call(this))}var n,r,o;return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&N(t,e)}(e,u["Component"]),n=e,(r=[{key:"render",value:function(){return c.a.createElement("span",null,c.a.createElement("a",{href:"/",style:{color:"pink",fontSize:50}},"首页"),c.a.createElement("a",{href:"/page2",style:{color:"pink",fontSize:50}},"次页"),c.a.createElement("a",{href:""},this.props.counter),this.props.children)}}])&&V(n.prototype,r),o&&V(n,o),e}(),z=Object(a.connect)(function(t){return{counter:t.counter,state:t}},function(t){return Object(f.bindActionCreators)(o,t)})(U);var L=function(){return c.a.createElement(i.Switch,null,c.a.createElement(z,null,c.a.createElement(m.Route,{exact:!0,path:"/",component:R}),c.a.createElement(m.Route,{exact:!0,path:"/page2",component:J})))};e.default=function(t,e){var n=d(JSON.parse(e.store));return function(e,r){return c.a.createElement(a.Provider,{store:n},c.a.createElement(i.StaticRouter,{location:t.url,context:r},c.a.createElement(L,null)))}}}])); --------------------------------------------------------------------------------