The directory structure

First, plan the directory structure according to the figure above: Articles store all markdown files in the year/month/day directory hierarchy, with the.md suffix public static resource directory, SRC Server Server code location site front-end code location config.ts homogeneous data styles.less Common style xxx. TSX page entry pages Specific page implementation Layout Overall layout Js TdTool configuration file, tdTool is a package based on WebPack2, refer to the subsequent introduction of tdTool. Tsconfig. json typescript compilation option configuration file

Tdtool configuration

Part of the WebPack configuration is built into tdTool. The following configuration is an extension configuration

Const siteConfig = new Config({entry: {home: './ SRC /site/home.tsx', articles: '. './dist/website', filename: isDebug ? '[name].js? [hash]' : '[name].[hash].js', minimize: ! IsDebug, sourceMap: isDebug, extends: [['less', {// less + postcss + extractTextPlugin is a combination used to parse and compile less extractCss: { filename: '[name].css?[hash]' } }]] }); Siteconfig. add(' rule-ts ', {// add Babel + ts loader test: /\.tsx? $/, exclude: /node_modules/, use: [{ loader: 'babel-loader', query: { cacheDirectory: true, babelrc: false, presets: [ 'es2015-ie', 'react', 'stage-2', ], plugins: [ 'transform-decorators-legacy', 'transform-class-properties', 'transform-runtime' ] } }, { loader: 'ts-loader' }] }); Siteconfig. add(// Add assetsPlugin export the asset. json file for the server to use 'plugin. assetsPlugin ', new assetsPlugin ({path: './dist/website', filename: 'assets.json', prettyPrint: true, }) ); /* Server configuration */ const serverConfig = new Config({entry: './ SRC /server/index.ts', dist: './dist/server', target: 'node', filename: 'main.js', devServer: isDebug, // dev mode tdTool will use this configuration and add hot load, multiple config can only configure one devServer sourceMap: true, externals: [/^\.\.\/website\/assets\.json$/, require('webpack-node-externals')()], extends: [['less', { target: 'node' }]] }); Serverconfig. add(' rule-ts ', {// Since the Node version supports most ES6, the server does not compile test: /\.tsx? $/, loader: 'ts-loader', exclude: /node_modules/ }); function loadCommon(config, key) { config.add('resolve.extensions', [".tsx", ".ts", ".js"]); Config. Add (' plugin. CleanWebpackPlugin 'new CleanWebpackPlugin ([key] and {root, / / verbose root directory: Dry: false // Enable file deletion})); Config. add('rule. Articles ', {test: /\.DOCS$/, loader: 'articles-loader', query: { root: path.join(__dirname, 'articles') } }); // This blog uses part of the bootstrap style and the icon config.add(' rules.glyphicons ', {test: /glyphicons-halflings-regular\.(woff|woff2|ttf|eot|svg)($|\?) /, use: [{ loader: 'url-loader', options: { limit: 10000, name: 'fonts/[name].[ext]' } }] }); / / IMAGE file loader config. Add (' rule. IMAGE, {test: / \. (PNG | JPG | jpeg | GIF)? $/i, loader: 'url-loader', query: { limit: 10000, name: '[name]-[hash:5].[ext]' } }); } loadCommon(siteConfig, 'website'); loadCommon(serverConfig, 'server'); module.exports = [siteConfig.resolve(), serverConfig.resolve()];Copy the code

homogeneous

The React-Router is not used this time

The client

Take the home page, for example

Entrance to the file

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import HomePage from './pages/home/index';
import configs from './configs';

ReactDOM.render(
  <HomePage pagination={configs.pagination}/>,
  document.getElementById('wrapper')
);Copy the code

Configs is the initialization data that hangs under window.__config__ and is injected by the server

The config.ts code is as follows

export default (<any>window).__CONFIG__;Copy the code

The service side

Home routing code example

router/home.ts

import * as Router from 'koa-router'; import articles from '.. /.. /articles.DOCS'; import * as R from 'ramda'; import * as React from 'react'; import * as ReactDOMServer from 'react-dom/server'; import Home from '.. /.. /site/pages/home/index'; import renderView from '.. /renderView'; const pageSize = 10; const home = new Router(); home.get('/', async ( ctx ) => { const current = ctx.query.page || 1; const offset = (current - 1) * pageSize; const ats = R.slice(offset, offset + pageSize)(articles.mdsArray); const total = articles.mdsArray.length; const pagination = { current, total, articles: ats }; ctx.body = renderView('home', { pagination, html: ReactDOMServer.renderToStaticMarkup(React.createElement(Home, { pagination })) }); }); export default home;Copy the code

The pagination object is the data required for the isomer of the front and back ends. The server passes it directly to the component via React. CreateElement (Home, {pagination}), and the client injects it in the following way

renderView.ts

Others corresponds to pagination in home.ts. As can be seen from the above code, this value is injected into window.__CONFIG__ for the front-end to use assets, which is the file mapping output by front-end compilation as follows:

{ "articles": { "js": "/articles.40099d3476b9654d29e2.js", "css": "/articles.css? 40099d3476b9654d29e2" }, "tags": { "js": "/tags.40099d3476b9654d29e2.js", "css": "/tags.css? 40099d3476b9654d29e2" }, "categories": { "js": "/categories.40099d3476b9654d29e2.js", "css": "/categories.css? 40099d3476b9654d29e2" }, "home": { "js": "/home.40099d3476b9654d29e2.js", "css": "/home.css? 40099d3476b9654d29e2" }, "about": { "js": "/about.40099d3476b9654d29e2.js", "css": "/about.css? 40099d3476b9654d29e2" } }Copy the code

Articles.DOCS is md article information collected through articles- Loader. What does the collected information include? React + Koa2 + Markdown + React + Markdown + Markdown