я писал некоторое время назадПрактика TypeScript в Node-проектах.
Есть объяснение, почему использоватьTS
, И вNode
Какова структура проекта в .
Но это был чисто интерфейсный проект, недавно мне довелось заниматься рефакторингом другого проекта, после последней практики я попробовалTS
Сладость, принесенная, без колебаний выберете использованиеTS
+React
провести рефакторинг этого проекта.
Эта реконструкция включает не толькоNode
рефакторинг (ранее это былоExpress
проект), а также рефакторинг фронтенда (ранее делалjQuery
управляемые многостраничные приложения).
Структура проекта
Поскольку в текущем проекте не планируется разделять переднюю и заднюю части (проект внутренней инструментальной платформы), общая структура основана на последнемNode
Структура проекта, с некоторыми дополнениями поверх негоFrontEnd
Структура каталогов:
.
├── README.md
├── copy-static-assets.ts
├── nodemon.json
├── package.json
+ ├── client-dist
+ │ ├── bundle.js
+ │ ├── bundle.js.map
+ │ ├── logo.png
+ │ └── vendors.dll.js
├── dist
├── src
│ ├── config
│ ├── controllers
│ ├── entity
│ ├── models
│ ├── middleware
│ ├── public
│ ├── app.ts
│ ├── server.ts
│ ├── types
+ │ ├── common
│ └── utils
+ ├── client-src
+ │ ├── components
+ │ │ └── Header.tsx
+ │ ├── conf
+ │ │ └── host.ts
+ │ ├── dist
+ │ ├── utils
+ │ ├── index.ejs
+ │ ├── index.tsx
+ │ ├── webpack
+ │ ├── package.json
+ │ └── tsconfig.json
+ ├── views
+ │ └── index.ejs
├── tsconfig.json
└── tslint.json
который отмечен зеленым (также может быть+
отображаемый номер) на этот раз добавлен заново.
вclient-dist
а такжеviews
на всем протяженииwebpack
Сгенерированы, фактические исходные файлы находятся вclient-src
Вниз.Нет затрат на разделение до и после разделения этой структуры.
Некоторые из следующих папок разделены ниже:
dir/file | desc |
---|---|
index.ejs |
вход в проектhtml файл, используяejs как движок рендеринга |
index.tsx |
вход в проектjs файл, используется суффиксtsx , по двум причинам:1. Мы будем использовать ts провестиReact разработка программы2. .tsx файл по сравнению с кодомicon лучше выглядишь :р |
tsconfig.json |
используется дляtsc Некоторые конфигурационные файлы для компиляции и выполнения |
components |
Каталог, в котором хранятся компоненты |
config |
Место, где хранятся различные элементы конфигурации, аналогично интерфейсу запроса.host или различные состоянияmap Отображение и тому подобное (можно понять, что объекты перечисления все здесь) |
utils |
Место, где хранятся некоторые общедоступные функции, и здесь должны быть размещены различные повторно используемые коды. |
dist |
Место хранения различных статических ресурсов, картинок и других файлов |
webpack |
Существуют различные средыwebpack команды сценария иdll поколение |
Попытка повторного использования кода между интерфейсом и сервером
На самом деле отсутствует новая папка, мы находимся вsrc
Добавлен новый каталогcommon
Каталог, этот каталог предназначен для хранения некоторых общедоступных функций и общедоступныхconfig
, отличный отutils
илиconfig
Дело в том, что код здесь разделяется между фронтендом и бекендом, поэтому функции здесь должны быть полностью свободны от каких-либо зависимостей от окружения и бизнес-логики.
Похожие числа на тысячные, форматирование даты или номер порта, который слушает сервис, эти коды, не содержащие никакой логики и не сильно зависящие от окружения, мы можем поставить их здесь.
Это также небольшая приятность, связанная с отсутствием разделения на переднюю и заднюю часть, и некоторый код может быть разделен до и после.
Для достижения такой конфигурации необходимо внести следующие изменения на основе вышеуказанных проектов:
-
src
внизutils
а такжеconfig
Некоторый код мигрирует вcommon
Под папкой он в основном используется для определения того, можно ли его использовать до и после - для сравнения предыдущего
node
Структурные воздействия сведены к минимуму, нам необходимоcommon
Добавить новый в папкуindex.ts
индексный файл, а вutils/index.ts
цитировать его ниже, так что дляnode
С точки зрения использования вам не нужно заботиться о том, исходит ли этот файл изutils
ещеcommon
// src/common/utils/comma.ts
export default (num: number): string => String(num).replace(/\B(?=(\d{3})+$)/g, ',')
// src/common/utils/index.ts
export { default as comma } from './comma'
// src/utils.index.ts
export * from '../common/utils'
// src/app.ts
import { comma } from './utils' // 并不需要关心是来自common还是来自utils
console.log(comma(1234567)) // 1,234,567
- затем настройте
webpack
изalias
свойства дляwebpack
может правильно найти свой путь
// client-src/webpack/base.js
module.exports = {
resolve: {
alias: {
'@Common': path.resolve(__dirname, '../../src/common'),
}
}
}
- Нам также необходимо настроить
tsconfig.json
используется дляvs code
Вы можете найти соответствующий каталог, иначе вам будет предложено в редактореcan't find module XXX
// client-src/tsconfig.json
{
"compilerOptions": {
"paths": {
// 用于引入某个`module`
"@Common/*": [
"../src/common/*"
]
}
}
}
- Наконец в
client-src/utils/index.ts
написать что-то вродеserver
окончание обработки
// client-src/utils/index.ts
export * from '@Common/utils'
// client-src/index.tsx
import { comma } from './utils'
console.log(comma(1234567)) // 1,234,567
Строительство окружающей среды
При использованииvs code
Развитие и использованиеESLint
Если его нужно изменитьTS
Грамматически поддерживаемые суффиксы, добавьтеtypescriptreact
некоторую обработку, это автоматически исправит некоторыеESLint
правило:
"eslint.validate": [
"javascript",
"javascriptreact",
{
"language": "typescript",
"autoFix": true
},
{
"language": "typescriptreact",
"autoFix": true
}
]
конфигурация веб-пакета
Потому что он используется в передней частиReact
, согласно нынешнему мейнстриму,webpack
Определенно необходимо.
не выбрал зрелогоcra
(create-react-app) для построения среды по следующим причинам:
-
webpack
Я не пробовал с тех пор как обновился до 4, сам хочу поиграть - комбинированный
TS
Помимо вещей внутри компании, будут некоторые ситуации с пользовательской конфигурацией, опасаясь, что вторичная разработка будет слишком громоздкой.
Но на самом деле конфигурации не так много.Фреймворк пользовательского интерфейса, выбранный для этой реконструкции, является реализацией Google Material:material-ui
И они использовалиjssчтобы написать стиль, чтобы он не включал предыдущий идиоматическийscss
наборloader
.
webpack
Он разделен на следующие файлы:
file | desc |
---|---|
common.js |
публичныйwebpack конфигурация, какenv такие варианты, как |
dll.js |
Используется для упаковки некоторых сторонних библиотек, которые не будут изменены заранее, чтобы ускорить эффективность компиляции во время разработки. |
base.js |
можно понимать какwebpack Базовый файл конфигурации, общийloader так же какplugins это здесь |
pro.js |
Специальная конфигурация для производственной среды (сжатие кода, загрузка ресурсов) |
dev.js |
Специальная конфигурация для среды разработки (source-map ) |
dll
Это очень старая процедура, и ее, вероятно, необходимо изменить следующим образом:
- создать отдельный
webpack
файл для генерацииdll
документ - в обычном
webpack
генерируется ссылкой в файлеdll
документ
// dll.js
{
entry: {
// 需要提前打包的库
vendors: [
'react',
'react-dom',
'react-router-dom',
'babel-polyfill',
],
},
output: {
filename: 'vendors.dll.js',
path: path.resolve(__dirname, '../../client-dist'),
// 输出时不要少了这个option
library: 'vendors_lib',
},
plugins: [
new webpack.DllPlugin({
context: __dirname,
// 向外抛出的`vendors.dll.js`代码的具体映射,引用`dll`文件的时候通过它来做映射关系的
path: path.join(__dirname, '../dist/vendors-manifest.json'),
name: 'vendors_lib',
})
]
}
// base.js
{
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('../dist/vendors-manifest.json'),
}),
]
}
так вwatch
файл, упаковка будет пропущенаverdors
Те пакеты, которые существуют в .
Следует отметить одну вещь: если вам в конечном итоге понадобится загрузить эти статические ресурсы, не забудьте включитьverdors.dll.js
загрузить вместе
При локальном развитииvendors
файлы не внедряются автоматически вhtml
шаблон, поэтому мы использовали другой плагин,add-asset-html-webpack-plugin.
В то же время вы также можете столкнуться сwebpack
Неограниченная переупаковка, для этого требуется настройкаignore
решать -.-:
// dev.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
{
plugins: [
// 将`ejs`模版文件放到目标文件夹,并注入入口`js`文件
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../index.ejs'),
filename: path.resolve(__dirname, '../../views/index.ejs'),
}),
// 将`vendors`文件注入到`ejs`模版中
new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname, '../../client-dist/vendors.dll.js'),
includeSourcemap: false,
}),
// 忽略`ejs`和`js`的文件变化,避免`webpack`无限重新打包的问题
new webpack.WatchIgnorePlugin([
/\.ejs$/,
/\.js$/,
]),
]
}
Конфигурация, связанная с TypeScript
TS
Конфигурация разделена на две части, однаwebpack
конфиг, другойtsconfig
Конфигурация.
прежде всегоwebpack
,противts
,tsx
файл мы используем дваloader
:
{
rules: [
{
test: /\.tsx?$/,
use: ['babel-loader', 'ts-loader'],
exclude: /node_modules/,
}
],
resolve: {
// 一定不要忘记配置ts tsx后缀
extensions: ['.tsx', '.ts', '.js'],
}
}
ts-loader
дляTS
Некоторые свойстваJS
совместимый синтаксис, затем выполнитеbabel
обрабатыватьreact/jsx
Соответствующий код, который в конечном итоге генерирует исполняемый файлJS
код.
Послеtsconfig
Конфигурация,ts-loader
Выполнение основано на конфигурации здесь, и общая конфигурация выглядит следующим образом:
{
"compilerOptions": {
"module": "esnext",
"target": "es6",
"allowSyntheticDefaultImports": true,
// import的相对起始路径
"baseUrl": ".",
"sourceMap": true,
// 构建输出目录,但因为使用了`webpack`,所以这个配置并没有什么卵用
"outDir": "../client-dist",
// 开启`JSX`模式,
// `preserve`的配置让`tsc`不会去处理它,而是使用后续的`babel-loader`进行处理
"jsx": "preserve",
"strict": true,
"moduleResolution": "node",
// 开启装饰器的使用
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
// `vs code`所需要的,在开发时找到对应的路径,真实的引用是在`webpack`中配置的`alias`
"paths": {
"@Common": [
"../src/common"
],
"@Common/*": [
"../src/common/*"
]
}
},
"exclude": [
"node_modules"
]
}
Конфигурация ESLint
Недавно наша команда была основана наairbnb
изESLint
Правила немного доработали, создав свои собственныеeslint-config-blued
также существуетreactа такжеtypescriptдве производные версии.
оESLint
файл конфигурации.eslintrc
, в этом проекте две копии. один является корневым каталогомblued-typescript
, другойclient-src
внизblued-react
+ blued-typescript
.
Поскольку корневой каталог больше используется дляnode
проект, поэтому нет необходимости ставитьreact
Какие зависимости также установлены.
# .eslintrc
extends: blued-typescript
# client-src/.eslintrc
extends:
- blued-react
- blued-typescript
Небольшая деталь, на которую стоит обратить внимание
потому что нашreact
а такжеtypescript
используется в версии реализацииparser
.
react
используетbabel-eslint,typescript
используетtypescript-eslint-parser.
ноparser
Может быть только один, изoption
Это видно из названияextends
,plugins
,rules
,прибытьparser
Нет множественного числа.
Итак, эти два плагина находятся вextends
Порядок, в котором становится критическим,babel
Я не могу понять это сейчасTS
синтаксис, но похожеbabel
Разработчик имеет поддержкуTS
избуду.
Но пока мы должны обеспечитьreact
спереди,typescript
после, вот такparser
будет использованоtypescript-eslint-parser
покрывать.
Модификация слоя узла
В дополнение к общему коду на обоих концах, упомянутому выше, вам также необходимо добавитьcontroller
используется, чтобы выплюнуть страницу, потому что с помощьюrouting-controllers
Эта библиотека, отображающая статическую страницу, очень хорошо инкапсулирована, нужно только изменить две страницы, одну для настройкиrender
Корневой каталог шаблона, другой используется для установки имени выдаваемого шаблона:
// controller/index.ts
import {
Get,
Controller,
Render,
} from 'routing-controllers'
@Controller('/')
export default class {
@Get('/')
@Render('index') // 指定一个模版的名字
async router() {
// 渲染页面时的一些变量
// 类似之前的 ctx.state = XXX
return {
title: 'First TypeScript React App',
}
}
}
// app.ts
import koaViews from 'koa-views'
// 添加模版所在的目录
// 以及使用的渲染引擎、文件后缀
app.use(koaViews(path.join(__dirname, '../views'), {
options: {
ext: 'ejs',
},
extension: 'ejs',
}))
Если страниц несколько, создайте несколько страниц дляRender
изts
Файл вроде
глубокая яма, обратите внимание
токrouting-controller
дляKoa
Поддержка не очень, (у оригинального автора естьKoa
не очень понимаю, что приводит кRender
После того, как соответствующий интерфейс будет запрошен один раз, все последующие интерфейсы будут напрямую возвращать файл шаблона, потому что шаблон отвечает за отрисовкуURL
При запуске он должен возвращать данные, но текущая обработка заключается в добавлении промежуточного программного обеспечения кKoa
В шаблоне, поэтому любые запросы будут возвращены в виде файла данных), поэтому@Render
не относится кKoa
водить машину.
Но я уже представилPR, прогнал тесткейс, и дождался слияния кода, но это план временной модификации, который предполагает порядок регистрации этой библиотеки для внешнего промежуточного ПО, поэтому дляapp.ts
Для этого требуются дополнительные модификации.
// app.ts 的修改
import 'reflect-metadata'
import Koa from 'koa'
import koaViews from 'koa-views'
import { useKoaServer } from 'routing-controllers'
import { distPath } from './config'
// 手动创建koa实例,然后添加`render`的中间件,确保`ctx.render`方法会在请求的头部就被添加进去
const koa = new Koa()
koa.use(koaViews(path.join(__dirname, '../views'), {
options: {
ext: 'ejs',
},
extension: 'ejs',
}))
// 使用`useKoaServer`而不是`createKoaServer`
const app = useKoaServer(koa, {
controllers: [`${__dirname}/controllers/**/*{.js,.ts}`],
})
// 后续的逻辑就都一样了
export default app
Конечно, это логика после выхода новой версии, и ее можно обойти на основе существующей структуры, но нельзя использовать@Render
Декоратор, оставь этоkoa-views
непосредственно использовать внутреннююconsolidate:
// controller/index.ts
// 这个修改不需要改动`app.ts`,可以直接使用`createKoaServer`
import {
Get,
Controller,
} from 'routing-controllers'
import cons from 'consolidate'
import path from 'path'
@Controller()
export default class {
@Get('/')
async router() {
// 直接在接口返回时获取模版渲染后的数据
return cons.ejs(path.resolve(__dirname, '../../views/index.ejs'), {
title: 'Example For TypeScript React App',
})
}
}
Текущий пример кода использует приведенную выше схему.
резюме
На данный момент построена полная архитектура проекта TS front-end и back-end (оставшаяся задача — залить код в скелет).
я обновил предыдущийtypescript-exmapleДобавлены некоторые внешние интерфейсы, используемые в этом рефакторинге.TS
+React
например, также включает@Render
некоторые совместимы.
TypeScript
отличная идея, решил N многоjavaScript
Досадная проблема.
Использование статического языка для разработки может не только повысить эффективность разработки, но и снизить вероятность ошибок.
в сочетании с мощнымvs code
, Наслаждайся этим.
при использованииTS
Если у вас есть какие-либо вопросы в процессе или есть идеи получше, добро пожаловать на общение и обсуждение.
One more things
Команда Blued front-end/Node набирает сотрудников. . Неполная средняя школа и средняя школа имеют HC
Координаты Чаоян Шуанцзин, имперской столицы, если интересно, свяжитесь со мной:
чат: github_jiasm
Почта:jiashunming@blued.com
Добро пожаловать, чтобы оставить свое резюме