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