TS + React
<p><span style="font-size: 1.5em">项目结构</span></p><p>因为目前项目是没有做前后分离的打算的(一个内部工具平台类的项目),所以大致结构就是基于上次<code>Node</code>项目的结构,在其之上添加了一些<code>FrontEnd</code>的目录结构:</p>
<pre class="diff"><code class="hljs">.
├── README.md
├── copy-static-assets.ts
├── nodemon.json
├── package.json
<span class="hljs-addition">+ ├── client-dist
<span class="hljs-addition">+ │ ├── bundle.js
<span class="hljs-addition">+ │ ├── bundle.js.map
<span class="hljs-addition">+ │ ├── logo.png
<span class="hljs-addition">+ │ └── vendors.dll.js
├── dist
├── src
│ ├── config
│ ├── controllers
│ ├── entity
│ ├── models
│ ├── middleware
│ ├── public
│ ├── app.ts
│ ├── server.ts
│ ├── types
<span class="hljs-addition">+ │ ├── common
│ └── utils
<span class="hljs-addition">+ ├── client-src
<span class="hljs-addition">+ │ ├── components
<span class="hljs-addition">+ │ │ └── Header.tsx
<span class="hljs-addition">+ │ ├── conf
<span class="hljs-addition">+ │ │ └── host.ts
<span class="hljs-addition">+ │ ├── dist
<span class="hljs-addition">+ │ ├── utils
<span class="hljs-addition">+ │ ├── index.ejs
<span class="hljs-addition">+ │ ├── index.tsx
<span class="hljs-addition">+ │ ├── webpack
<span class="hljs-addition">+ │ ├── package.json
<span class="hljs-addition">+ │ └── tsconfig.json
<span class="hljs-addition">+ ├── views
<span class="hljs-addition">+ │ └── index.ejs
├── tsconfig.json
└── tslint.json</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>其中标绿(也可能是一个<code>+</code>号显示)的文件为本次新增的。<br>其中<code>client-dist</code>与<code>views</code>都是通过<code>webpack</code>生成的,实际的源码文件都在<code>client-src</code>下。<em>就这个结构拆分前后分离其实没有什么成本</em><br>在下边分了大概这样的一些文件夹:</p>
<table>
<thead>
<tr class="header"><th>dir/file</th><th>desc</th></tr>
</thead>
<tbody>
<tr class="odd">
<td><code>index.ejs</code></td>
<td>项目的入口<code>html</code>文件,采用<code>ejs</code>作为渲染引擎</td>
</tr>
<tr class="even">
<td><code>index.tsx</code></td>
<td>项目的入口<code>js</code>文件,后缀使用<code>tsx</code>,原因有二:<br>1. 我们会使用<code>ts</code>进行<code>React</code>程序的开发<br>2. <code>.tsx</code>文件在vs code上的<code>icon</code>比较好看 :p</td>
</tr>
<tr class="odd">
<td><code>tsconfig.json</code></td>
<td>是用于<code>tsc</code>编译执行的一些配置文件</td>
</tr>
<tr class="even">
<td><code>components</code></td>
<td>组件存放的目录</td>
</tr>
<tr class="odd">
<td><code>config</code></td>
<td>各种配置项存放的位置,类似请求接口的<code>host</code>或者各种状态的<code>map</code>映射之类的(可以理解为枚举对象们都在这里)</td>
</tr>
<tr class="even">
<td><code>utils</code></td>
<td>一些公共函数存放的位置,各种可复用的代码都应该放在这里</td>
</tr>
<tr class="odd">
<td><code>dist</code></td>
<td>各种静态资源的存放位置,图片之类文件</td>
</tr>
<tr class="even">
<td><code>webpack</code></td>
<td>里边存放了各种环境的<code>webpack</code>脚本命令以及<code>dll</code>的生成</td>
</tr>
</tbody>
</table>
<h2 id="前后端复用代码的一个尝试">前后端复用代码的一个尝试</h2>
<p>实际上边还漏掉了一个新增的文件夹,我们在<code>src</code>目录下新增了一个<code>common</code>目录,这个目录是存放一些公共的函数和公共的<code>config</code>,不同于<code>utils</code>或者<code>config</code>的是,这里的代码是前后端共享的,所以这里边的函数一定要是完全的不包含任何环境依赖,不包含任何业务逻辑的。</p>
<p>类似的数字千分位,日期格式化,抑或是服务监听的端口号,这些不包含任何逻辑,也对环境没有强依赖的代码,我们都可以放在这里。<br>这也是没有做前后分离带来的一个小甜头吧,前后可以共享一部分代码。</p>
<p>要实现这样的配置,基于上述项目需要修改如下几处:</p>
<ol>
<li><code>src</code>下的<code>utils</code>和<code>config</code>部分代码迁移到<code>common</code>文件夹下,主要是用于区分是否可前后通用</li>
<li>为了将对之前<code>node</code>结构方面的影响降至最低,我们需要在<code>common</code>文件夹下新增一个<code>index.ts</code>索引文件,并在<code>utils/index.ts</code>下引用它,这样对于<code>node</code>方面使用来讲,并不需要关心这个文件是来自<code>utils</code>还是<code>common</code></li>
</ol>
<pre class="javascript"><code class="hljs"><span class="hljs-comment">// src/common/utils/comma.ts
<span class="hljs-keyword">export <span class="hljs-keyword">default (num: number): <span class="hljs-function"><span class="hljs-params">string => <span class="hljs-built_in">String(num).replace(<span class="hljs-regexp">/\B(?=(\d{3})+$)/g, <span class="hljs-string">',')
<span class="hljs-comment">// src/common/utils/index.ts
<span class="hljs-keyword">export { <span class="hljs-keyword">default <span class="hljs-keyword">as comma } <span class="hljs-keyword">from <span class="hljs-string">'./comma'
<span class="hljs-comment">// src/utils.index.ts
<span class="hljs-keyword">export * <span class="hljs-keyword">from <span class="hljs-string">'../common/utils'
<span class="hljs-comment">// src/app.ts
<span class="hljs-keyword">import { comma } <span class="hljs-keyword">from <span class="hljs-string">'./utils' <span class="hljs-comment">// 并不需要关心是来自common还是来自utils
<span class="hljs-built_in">console.log(comma(<span class="hljs-number">1234567)) <span class="hljs-comment">// 1,234,567</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<ol>
<li>然后是配置<code>webpack</code>的<code>alias</code>属性,用于<code>webpack</code>能够正确的找到其路径</li>
</ol>
<pre class="javascript"><code class="hljs"><span class="hljs-comment">// client-src/webpack/base.js
<span class="hljs-built_in">module.exports = {
<span class="hljs-attr">resolve: {
<span class="hljs-attr">alias: {
<span class="hljs-string">'@Common': path.resolve(__dirname, <span class="hljs-string">'../../src/common'),
}
}
}</span></span></span></span></span></span></code></pre>
<ol>
<li>同时我们还需要配置<code>tsconfig.json</code>用于<code>vs code</code>可以找到对应的目录,不然会在编辑器中提示<code>can't find module XXX</code></li>
</ol>
<pre class="javascript"><code class="hljs"><span class="hljs-comment">// client-src/tsconfig.json
{
<span class="hljs-string">"compilerOptions": {
<span class="hljs-string">"paths": {
<span class="hljs-comment">// 用于引入某个`module`
<span class="hljs-string">"@Common/*": [
<span class="hljs-string">"../src/common/*"
]
}
}
}</span></span></span></span></span></span></code></pre>
<ol>
<li>最后在<code>client-src/utils/index.ts</code>写上类似<code>server</code>端的处理就可以了</li>
</ol>
<pre class="javascript"><code class="hljs"><span class="hljs-comment">// client-src/utils/index.ts
<span class="hljs-keyword">export * <span class="hljs-keyword">from <span class="hljs-string">'@Common/utils'
<span class="hljs-comment">// client-src/index.tsx
<span class="hljs-keyword">import { comma } <span class="hljs-keyword">from <span class="hljs-string">'./utils'
<span class="hljs-built_in">console.log(comma(<span class="hljs-number">1234567)) <span class="hljs-comment">// 1,234,567</span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h2 id="环境的搭建">环境的搭建</h2>
<p><em>如果使用<code>vs code</code>进行开发,而且使用了<code>ESLint</code>的话,需要修改<code>TS</code>语法支持的后缀,添加<code>typescriptreact</code>的一些处理,这样才会自动修复一些<code>ESLint</code>的规则:</em></p>
<pre class="javascript"><code class="hljs"><span class="hljs-string">"eslint.validate": [
<span class="hljs-string">"javascript",
<span class="hljs-string">"javascriptreact",
{
<span class="hljs-string">"language": <span class="hljs-string">"typescript",
<span class="hljs-string">"autoFix": <span class="hljs-literal">true
},
{
<span class="hljs-string">"language": <span class="hljs-string">"typescriptreact",
<span class="hljs-string">"autoFix": <span class="hljs-literal">true
}
]</span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h3 id="webpack的配置">webpack的配置</h3>
<p>因为在前端使用了<code>React</code>,按照目前的主流,<code>webpack</code>肯定是必不可少的。<br>并没有选择成熟的<code>cra</code>(create-react-app)来进行环境搭建,原因有下:</p>
<ol>
<li><code>webpack</code>更新到4以后并没有尝试过,想自己耍一耍</li>
<li>结合着<code>TS</code>以及公司内部的东西,会有一些自定义配置情况的出现,担心二次开发太繁琐</li>
</ol>
<p>但是其实也没有太多的配置,本次重构选用的UI框架为Google Material的实现:material-ui<br>而他们采用的是jss 来进行样式的编写,所以也不会涉及到之前惯用的<code>scss</code>的那一套<code>loader</code>了。</p>
<p><code>webpack</code>分了大概如下几个文件:</p>
<table>
<thead>
<tr class="header"><th>file</th><th>desc</th></tr>
</thead>
<tbody>
<tr class="odd">
<td><code>common.js</code></td>
<td>公共的<code>webpack</code>配置,类似<code>env</code>之类的选项</td>
</tr>
<tr class="even">
<td><code>dll.js</code></td>
<td>用于将一些不会修改的第三方库进行提前打包,加快开发时编译效率</td>
</tr>
<tr class="odd">
<td><code>base.js</code></td>
<td>可以理解为是<code>webpack</code>的基础配置文件,通用的<code>loader</code>以及<code>plugins</code>在这里</td>
</tr>
<tr class="even">
<td><code>pro.js</code></td>
<td>生产环境的特殊配置(代码压缩、资源上传)</td>
</tr>
<tr class="odd">
<td><code>dev.js</code></td>
<td>开发环境的特殊配置(<code>source-map</code>)</td>
</tr>
</tbody>
</table>
<p><code>dll</code>是一个很早之前的套路了,大概需要修改这么几处:</p>
<ol>
<li>创建一个单独的<code>webpack</code>文件,用于生成<code>dll</code>文件</li>
<li>在普通的<code>webpack</code>文件中进行引用生成的<code>dll</code>文件</li>
</ol>
<pre class="javascript"><code class="hljs"><span class="hljs-comment">// dll.js
{
<span class="hljs-attr">entry: {
<span class="hljs-comment">// 需要提前打包的库
vendors: [
<span class="hljs-string">'react',
<span class="hljs-string">'react-dom',
<span class="hljs-string">'react-router-dom',
<span class="hljs-string">'babel-polyfill',
],
},
<span class="hljs-attr">output: {
<span class="hljs-attr">filename: <span class="hljs-string">'vendors.dll.js',
<span class="hljs-attr">path: path.resolve(__dirname, <span class="hljs-string">'../../client-dist'),
<span class="hljs-comment">// 输出时不要少了这个option
library: <span class="hljs-string">'vendors_lib',
},
<span class="hljs-attr">plugins: [
<span class="hljs-keyword">new webpack.DllPlugin({
<span class="hljs-attr">context: __dirname,
<span class="hljs-comment">// 向外抛出的`vendors.dll.js`代码的具体映射,引用`dll`文件的时候通过它来做映射关系的
path: path.join(__dirname, <span class="hljs-string">'../dist/vendors-manifest.json'),
<span class="hljs-attr">name: <span class="hljs-string">'vendors_lib',
})
]
}
<span class="hljs-comment">// base.js
{
<span class="hljs-attr">plugins: [
<span class="hljs-keyword">new webpack.DllReferencePlugin({
<span class="hljs-attr">context: __dirname,
<span class="hljs-attr">manifest: <span class="hljs-built_in">require(<span class="hljs-string">'../dist/vendors-manifest.json'),
}),
]
}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>这样在<code>watch</code>文件时,打包就会跳过<code>verdors</code>中存在的那些包了。<br><strong>有一点要注意的,如果最终需要上传这些静态资源,记得连带着<code>verdors.dll.js</code>一并上传</strong></p>
<p>在本地开发时,<code>vendors</code>文件并不会自动注入到<code>html</code>模版中去,所以我们有用到了另一个插件,add-asset-html-webpack-plugin。<br>同时在使用中可能还会遇到<code>webpack</code>无限次数的重新打包,这个需要配置<code>ignore</code>来解决-.-:</p>
<pre class="javascript"><code class="hljs"><span class="hljs-comment">// dev.js
<span class="hljs-keyword">const HtmlWebpackPlugin = <span class="hljs-built_in">require(<span class="hljs-string">'html-webpack-plugin')
<span class="hljs-keyword">const AddAssetHtmlPlugin = <span class="hljs-built_in">require(<span class="hljs-string">'add-asset-html-webpack-plugin')
{
<span class="hljs-attr">plugins: [
<span class="hljs-comment">// 将`ejs`模版文件放到目标文件夹,并注入入口`js`文件
<span class="hljs-keyword">new HtmlWebpackPlugin({
<span class="hljs-attr">template: path.resolve(__dirname, <span class="hljs-string">'../index.ejs'),
<span class="hljs-attr">filename: path.resolve(__dirname, <span class="hljs-string">'../../views/index.ejs'),
}),
<span class="hljs-comment">// 将`vendors`文件注入到`ejs`模版中
<span class="hljs-keyword">new AddAssetHtmlPlugin({
<span class="hljs-attr">filepath: path.resolve(__dirname, <span class="hljs-string">'../../client-dist/vendors.dll.js'),
<span class="hljs-attr">includeSourcemap: <span class="hljs-literal">false,
}),
<span class="hljs-comment">// 忽略`ejs`和`js`的文件变化,避免`webpack`无限重新打包的问题
<span class="hljs-keyword">new webpack.WatchIgnorePlugin([
<span class="hljs-regexp">/\.ejs$/,
/\.js$/,
]),
]
}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h3 id="typescript相关的配置">TypeScript相关的配置</h3>
<p><code>TS</code>的配置分了两块,一个是<code>webpack</code>的配置,另一个是<code>tsconfig</code>的配置。</p>
<p>首先是<code>webpack</code>,针对<code>ts</code>、<code>tsx</code>文件我们使用了两个<code>loader</code>:</p>
<pre class="javascript"><code class="hljs">{
<span class="hljs-attr">rules: [
{
<span class="hljs-attr">test: <span class="hljs-regexp">/\.tsx?$/,
<span class="hljs-attr">use: [<span class="hljs-string">'babel-loader', <span class="hljs-string">'ts-loader'],
<span class="hljs-attr">exclude: <span class="hljs-regexp">/node_modules/,
}
],
<span class="hljs-attr">resolve: {
<span class="hljs-comment">// 一定不要忘记配置ts tsx后缀
extensions: [<span class="hljs-string">'.tsx', <span class="hljs-string">'.ts', <span class="hljs-string">'.js'],
}
}</span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p><code>ts-loader</code>用于将<code>TS</code>的一些特性转换为<code>JS</code>兼容的语法,然后执行<code>babel</code>进行处理<code>react/jsx</code>相关的代码,最终生成可执行的<code>JS</code>代码。</p>
<p>然后是<code>tsconfig</code>的配置,<code>ts-loader</code>的执行是依托于这里的配置的,大致的配置如下:</p>
<pre class="javascript"><code class="hljs">{
<span class="hljs-string">"compilerOptions": {
<span class="hljs-string">"module": <span class="hljs-string">"esnext",
<span class="hljs-string">"target": <span class="hljs-string">"es6",
<span class="hljs-string">"allowSyntheticDefaultImports": <span class="hljs-literal">true,
<span class="hljs-comment">// import的相对起始路径
<span class="hljs-string">"baseUrl": <span class="hljs-string">".",
<span class="hljs-string">"sourceMap": <span class="hljs-literal">true,
<span class="hljs-comment">// 构建输出目录,但因为使用了`webpack`,所以这个配置并没有什么卵用
<span class="hljs-string">"outDir": <span class="hljs-string">"../client-dist",
<span class="hljs-comment">// 开启`JSX`模式,
<span class="hljs-comment">// `preserve`的配置让`tsc`不会去处理它,而是使用后续的`babel-loader`进行处理
<span class="hljs-string">"jsx": <span class="hljs-string">"preserve",
<span class="hljs-string">"strict": <span class="hljs-literal">true,
<span class="hljs-string">"moduleResolution": <span class="hljs-string">"node",
<span class="hljs-comment">// 开启装饰器的使用
<span class="hljs-string">"experimentalDecorators": <span class="hljs-literal">true,
<span class="hljs-string">"emitDecoratorMetadata": <span class="hljs-literal">true,
<span class="hljs-comment">// `vs code`所需要的,在开发时找到对应的路径,真实的引用是在`webpack`中配置的`alias`
<span class="hljs-string">"paths": {
<span class="hljs-string">"@Common": [
<span class="hljs-string">"../src/common"
],
<span class="hljs-string">"@Common/*": [
<span class="hljs-string">"../src/common/*"
]
}
},
<span class="hljs-string">"exclude": [
<span class="hljs-string">"node_modules"
]
}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h3 id="eslint的配置">ESLint的配置</h3>
<p>最近这段时间,我们团队基于<code>airbnb</code>的<code>ESLint</code>规则进行了一些自定义,创建了自家的eslint-config-blued<br>同时还存在了react和typescript的两个衍生版本。</p>
<p>关于<code>ESLint</code>的配置文件<code>.eslintrc</code>,在本项目中存在两份。一个是根目录的<code>blued-typescript</code>,另一个是<code>client-src</code>下的<code>blued-react</code> + <code>blued-typescript</code>。<br>因为根目录的更多用于<code>node</code>项目,所以没必要把<code>react</code>什么的依赖也装进来。</p>
<pre class="yaml"><code class="hljs yaml"><span class="hljs-comment"># .eslintrc
<span class="hljs-attr">extends: <span class="hljs-string">blued-typescript
<span class="hljs-comment"># client-src/.eslintrc
<span class="hljs-attr">extends:
<span class="hljs-bullet">- <span class="hljs-string">blued-react
<span class="hljs-bullet">- <span class="hljs-string">blued-typescript</span></span></span></span></span></span></span></span></span></code></pre>
<p><strong>一个需要注意的小细节</strong><br>因为我们的<code>react</code>与<code>typescript</code>实现版本中都用到了<code>parser</code>。<br><code>react</code>使用的是babel-eslint,<code>typescript</code>使用的是typescript-eslint-parser。<br>但是<code>parser</code>只能有一个,从<code>option</code>的命名中就可以看出<code>extends</code>、<code>plugins</code>、<code>rules</code>,到了<code>parser</code>就没有复数了。<br>所以这两个插件在<code>extends</code>中的顺序就变得很关键,<code>babel</code>现在并不能理解<code>TS</code>的语法,但好像<code>babel</code>开发者有支持<code>TS</code>的意愿。<br>但就目前来说,一定要保证<code>react</code>在前,<code>typescript</code>在后,这样<code>parser</code>才会使用<code>typescript-eslint-parser</code>来进行覆盖。</p>
<h3 id="node层的修改">node层的修改</h3>
<p>除了上边提到的两端公用代码以外,还需要添加一个<code>controller</code>用于吐页面,因为使用的是<code>routing-controllers</code>这个库,渲染一个静态页面被封装的非常棒,仅仅需要修改两个页面,一个用于设置<code>render</code>模版的根目录,另一个用来设置要吐出来的模版名称:</p>
<pre class="javascript"><code class="hljs"><span class="hljs-comment">// controller/index.ts
<span class="hljs-keyword">import {
Get,
Controller,
Render,
} <span class="hljs-keyword">from <span class="hljs-string">'routing-controllers'
@Controller(<span class="hljs-string">'/')
<span class="hljs-keyword">export <span class="hljs-keyword">default <span class="hljs-class"><span class="hljs-keyword">class {
@Get(<span class="hljs-string">'/')
@Render(<span class="hljs-string">'index') <span class="hljs-comment">// 指定一个模版的名字
<span class="hljs-keyword">async router() {
<span class="hljs-comment">// 渲染页面时的一些变量
<span class="hljs-comment">// 类似之前的 ctx.state = XXX
<span class="hljs-keyword">return {
<span class="hljs-attr">title: <span class="hljs-string">'First TypeScript React App',
}
}
}
<span class="hljs-comment">// app.ts
<span class="hljs-keyword">import koaViews <span class="hljs-keyword">from <span class="hljs-string">'koa-views'
<span class="hljs-comment">// 添加模版所在的目录
<span class="hljs-comment">// 以及使用的渲染引擎、文件后缀
app.use(koaViews(path.join(__dirname, <span class="hljs-string">'../views'), {
<span class="hljs-attr">options: {
<span class="hljs-attr">ext: <span class="hljs-string">'ejs',
},
<span class="hljs-attr">extension: <span class="hljs-string">'ejs',
}))</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p><em>如果是多个页面,那就创建多个用来<code>Render</code>的<code>ts</code>文件就好了</em></p>
<h4 id="深坑注意">深坑,注意</h4>
<p>目前的<code>routing-controller</code>对于<code>Koa</code>的支持还不是很好,(原作者对<code>Koa</code>并不是很了解,导致<code>Render</code>对应的接口被请求一次以后,后续所有的其他的接口都会直接返回该模版文件,原因是在负责模版渲染的<code>URL</code>触发时,本应返回数据,但是目前的处理却是添加了一个中间件到<code>Koa</code>中,所以任何请求都会将该模版文件作为数据来返回)所以<code>@Render</code>并不能适用于<code>Koa</code>驱动。<br>不过我已经提交了PR了,跑通了测试用例,坐等被合并代码,但是这是一个临时的修改方案,涉及到这个库针对外部中间件注册的顺序问题,所以对于<code>app.ts</code>还要有额外的修改才能够实现。</p>
<pre class="javascript"><code class="hljs"><span class="hljs-comment">// app.ts 的修改
<span class="hljs-keyword">import <span class="hljs-string">'reflect-metadata'
<span class="hljs-keyword">import Koa <span class="hljs-keyword">from <span class="hljs-string">'koa'
<span class="hljs-keyword">import koaViews <span class="hljs-keyword">from <span class="hljs-string">'koa-views'
<span class="hljs-keyword">import { useKoaServer } <span class="hljs-keyword">from <span class="hljs-string">'routing-controllers'
<span class="hljs-keyword">import { distPath } <span class="hljs-keyword">from <span class="hljs-string">'./config'
<span class="hljs-comment">// 手动创建koa实例,然后添加`render`的中间件,确保`ctx.render`方法会在请求的头部就被添加进去
<span class="hljs-keyword">const koa = <span class="hljs-keyword">new Koa()
koa.use(koaViews(path.join(__dirname, <span class="hljs-string">'../views'), {
<span class="hljs-attr">options: {
<span class="hljs-attr">ext: <span class="hljs-string">'ejs',
},
<span class="hljs-attr">extension: <span class="hljs-string">'ejs',
}))
<span class="hljs-comment">// 使用`useKoaServer`而不是`createKoaServer`
<span class="hljs-keyword">const app = useKoaServer(koa, {
<span class="hljs-attr">controllers: [<span class="hljs-string">`<span class="hljs-subst">${__dirname}/controllers/**/*{.js,.ts}`],
})
<span class="hljs-comment">// 后续的逻辑就都一样了
<span class="hljs-keyword">export <span class="hljs-keyword">default app</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>当然,这个是新版发出以后的逻辑了,基于现有的结构也可以绕过去,但是就不能使用<code>@Render</code>装饰器了,抛开<code>koa-views</code>直接使用内部的consolidate:</p>
<pre class="javascript"><code class="hljs"><span class="hljs-comment">// controller/index.ts
<span class="hljs-comment">// 这个修改不需要改动`app.ts`,可以直接使用`createKoaServer`
<span class="hljs-keyword">import {
Get,
Controller,
} <span class="hljs-keyword">from <span class="hljs-string">'routing-controllers'
<span class="hljs-keyword">import cons <span class="hljs-keyword">from <span class="hljs-string">'consolidate'
<span class="hljs-keyword">import path <span class="hljs-keyword">from <span class="hljs-string">'path'
@Controller()
<span class="hljs-keyword">export <span class="hljs-keyword">default <span class="hljs-class"><span class="hljs-keyword">class {
@Get(<span class="hljs-string">'/')
<span class="hljs-keyword">async router() {
<span class="hljs-comment">// 直接在接口返回时获取模版渲染后的数据
<span class="hljs-keyword">return cons.ejs(path.resolve(__dirname, <span class="hljs-string">'../../views/index.ejs'), {
<span class="hljs-attr">title: <span class="hljs-string">'Example For TypeScript React App',
})
}
}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p><em>目前的示例代码采用的上边的方案</em></p>
<h2 id="小结">小结</h2>
<p>至此,一个完整的TS前后端项目架构就已经搭建完成了(剩下的任务就是往骨架里边填代码了)。<br>我已经更新了之前的typescript-exmaple 在里边添加了本次重构所使用的一些前端<code>TS</code>+<code>React</code>的示例,还包括针对<code>@Render</code>的一些兼容。</p>
<p><code>TypeScript</code>是一个很棒的想法,解决了N多<code>javaScript</code>种令人诟病的问题。<br>使用静态语言来进行开发不仅能够提高开发的效率,同时还能降低错误出现的几率。<br>结合着强大的<code>vs code</code>,Enjoy it.</p>
<p>如果在使用<code>TS</code>的过程中有什么问题、或者有什么更好的想法,欢迎来沟通讨论。</p><br><br>
来源:https://www.cnblogs.com/JKHao/p/12259385.html
頁:
[1]