피닉스와 React: 올바른 길™
47335 단어 elixirreactphoenixjavascript
저의 TypeScript 가이드 2부를 찾고 계신 분들은 안심하세요!그것은 다음 주 내에 완성될 것이다.
나는 최근에 Elixir과 자주 교제한다.최근에 한 친구가 저에게 Discord 엔지니어링팀의 this blog post을 보여주었습니다. 그들은 어떻게 Elixir의 힘을 통해 그들의 플랫폼을 확장하는지 이야기했습니다. 읽은 후에 저는 한번 시도해 보겠다고 확신합니다.만약 네가 이 언어를 배우고 싶다면, 너는 Node에서 왔다.나처럼 네가 보러 가자고 건의한다.
만약 루비가 Rails, PHP가 Laravel, 그러면 Elixir는 Phoenix이 있다.만약 당신이 이전에 Rails를 사용한 적이 있다면, 당신은 마치 돌아온 것처럼 느낄 것이다.이것은 전형적인 웹 프레임워크의 기본 요소를 가지고 있다. 비록 Channels 같은 간결한 부가 기능이 있지만 이것은 플러그인을 사용하여 웹 응용 프로그램을 구축하는 데 더욱 쉽다.
나의 이상적인 웹 응용 프로그램 창고는 보통 React 전단을 포함한다.그래서 자연스럽습니다. React 전단으로 Phoenix 응용 프로그램을 구축하는 방법을 알고 싶습니다.불행하게도 봉황성과 React를 세우는 것은 많은 사람들이 생각하는 것처럼 그렇게 간단하지 않다.내가 인터넷에서 본 거의 모든 안내서는 하나의 React 구성 요소를 렌더링하는 것만 다루고 루트와 API 가져오기 등 기본적인 내용은 포함하지 않는다.나는 한동안 걸렸지만, 마지막으로, 나는 실제적으로 실행할 수 있는 설정을 찾았다™.
만약 네가 나와 같다면, 네가 도대체 어떻게 그것을 일하게 했는지 계속 알고 싶다면, 나는 너에게 어떻게 하는지 알려줄 것이다.이것이 영원히 이 문제에 대답할 수 있기를 바란다.
TL;박사
만약 네가 읽는 것을 좋아하지 않는다면, 나는 이미 이 안내서의 최종 결과 here을 준비했다.모든 설정이 완료되면 다음 스택이 있는 작업 Phoenix 설정이 있어야 합니다.
^1.7.4
)^10.15.0
)^6.4.1
)^1.4.0
)^16.7.0
)^3.0.0
)^4.0.0
)입문
이 안내서에서, 나는 당신이 이미 Elixir, Phoenix과 Node.js을 설치했다고 가정합니다.아직 없으면 새 탭에서 위의 링크를 열고 실행하십시오.걱정 마, 기다릴게.
우리는 또한 Phoenix 1.4를 사용할 것이다. 이것은 본문을 작성할 때 사용할 수 있는 최신 버전이다.
견본
우리는 우리가 사용할 구축 환경을 포함한 새로운 피닉스 프로젝트를 세울 것이다.
버전 1.4부터 Phoenix는 기본적으로 Webpack을 첨부합니다.다음 명령을 실행하면 JS 바인딩을 지원하는 Phoenix 설치 프로그램이 내장되어 있습니다.
$ mix phx.new phoenix_react_playground
의존 항목을 가져오고 설치할 것인지 물었을 때, "아니오"라고 대답하십시오. 잠시 후에 다시 토론합시다.기본적으로
package.json
파일, 패키지 설정, .babelrc
파일은 프로젝트 루트가 아닌 assets/
폴더에 있습니다.Visual Studio Code과 같은 IDE를 망칠 수 있기 때문에 이것은 결코 이상적이지 않다.따라서 프로젝트 루트로 이동합시다.$ cd phoenix_react_playground
$ mv assets/package.json .
$ mv assets/webpack.config.js .
$ mv assets/.babelrc .
이것은 Phoenix가 제공하는 기본 설정을 변경해야 한다는 것을 의미합니다..gitignore
@@ -26,7 +26,7 @@ phoenix_react_playground-*.tar
npm-debug.log
# The directory NPM downloads your dependencies sources to.
-/assets/node_modules/
+node_modules/
# Since we are building assets from assets/,
# we ignore priv/static. You may want to comment
package.json
@@ -6,8 +6,8 @@
"watch": "webpack --mode development --watch"
},
"dependencies": {
- "phoenix": "file:../deps/phoenix",
- "phoenix_html": "file:../deps/phoenix_html"
+ "phoenix": "file:deps/phoenix",
+ "phoenix_html": "file:deps/phoenix_html"
},
"devDependencies": {
"@babel/core": "^7.0.0",
@@ -18,7 +18,7 @@
"mini-css-extract-plugin": "^0.4.0",
"optimize-css-assets-webpack-plugin": "^4.0.0",
"uglifyjs-webpack-plugin": "^1.2.4",
- "webpack": "4.4.0",
- "webpack-cli": "^2.0.10"
+ "webpack": "4.28.4",
+ "webpack-cli": "^3.2.1"
}
}
webpack.config.js
@@ -13,11 +13,11 @@ module.exports = (env, options) => ({
]
},
entry: {
- './js/app.js': ['./js/app.js'].concat(glob.sync('./vendor/**/*.js'))
+ app: './assets/js/app.js'
},
output: {
filename: 'app.js',
- path: path.resolve(__dirname, '../priv/static/js')
+ path: path.resolve(__dirname, 'priv/static/js')
},
module: {
rules: [
@@ -36,6 +36,10 @@ module.exports = (env, options) => ({
},
plugins: [
new MiniCssExtractPlugin({ filename: '../css/app.css' }),
- new CopyWebpackPlugin([{ from: 'static/', to: '../' }])
- ]
+ new CopyWebpackPlugin([{ from: 'assets/static/', to: '../' }])
+ ],
+ resolve: {
+ // Add '.ts' and '.tsx' as resolvable extensions.
+ extensions: ['.ts', '.tsx', '.js', '.jsx', '.json']
+ }
});
상기 패키지 설정은 이상적인 피닉스 설정, 즉 assets/
폴더에 귀속되지 않은 자산을 설치하는 데 적용된다.Phoenix가 관찰자로서 Webpack 명령을 제대로 실행하는지 확인해야 합니다.이를 위해 config/dev.exs
을 다음과 같이 수정합니다.- watchers: []
+ watchers: [
+ {"node", [
+ "node_modules/webpack/bin/webpack.js",
+ "--watch-stdin",
+ "--colors"
+ ]}
+ ]
모든 것이 정상인지 확인하려면 다음 명령을 실행하십시오.$ mix deps.get
$ npm install
모든 것이 정상입니까?좋다이제 TypeScript 환경을 설정합니다.우선 Babel에 TypeScript+React 사전 설정을 설치하고
.babelrc
에 넣습니다.$ yarn add --dev @babel/preset-react @babel/preset-typescript @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread typescript
@@ -1,5 +1,10 @@
{
- "presets": [
- "@babel/preset-env"
- ]
-}
+ "presets": [
+ "@babel/preset-env",
+ "@babel/preset-react",
+ "@babel/preset-typescript"
+ ],
+ "plugins": [
+ "@babel/plugin-proposal-class-properties",
+ "@babel/plugin-proposal-object-rest-spread"
+ ]
+}
그런 다음 표준 tsconfig.json
파일을 만들고 다음 내용으로 채웁니다.{
"compilerOptions": {
"allowJs": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"isolatedModules": true,
"lib": ["dom", "esnext"],
"jsx": "preserve",
"target": "es2016",
"module": "esnext",
"moduleResolution": "node",
"preserveConstEnums": true,
"removeComments": false,
"sourceMap": true,
"strict": true
},
"include": ["./**/*.ts", "./**/*.tsx"]
}
마지막으로 우리의 패키지 설정을 수정하여 babel-loader
이 JS와 TS 파일을 받아들일 수 있도록 합니다.웹 페이지 항목 파일의 확장자를 바꾸는 것을 잊지 마세요!@@ -13,7 +13,7 @@ module.exports = (env, options) => ({
]
},
entry: {
- app: './assets/js/app.js'
+ app: './assets/js/app.tsx'
},
output: {
filename: 'app.js',
@@ -22,7 +22,7 @@ module.exports = (env, options) => ({
module: {
rules: [
{
- test: /\.js$/,
+ test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
템플릿 파일이 만들어지면 Phoenix 프로젝트의 폴더 구조는 다음과 같습니다.phoenix_react_playground/
├── assets/
│ ├── js/
│ │ ├── ...
│ │ └── app.tsx
│ ├── scss/
│ │ ├── ...
│ │ └── app.scss
│ └── static/
│ ├── images/
│ │ └── ...
│ ├── favicon.ico
│ └── robots.txt
├── config/
│ └── ...
├── lib/
│ └── ...
├── priv/
│ └── ...
├── test/
│ └── ...
├── .gitignore
├── mix.exs
├── package.json
├── README.md
├── tsconfig.json
└── webpack.config.js
React 설정
이제 우리는 정확한 방식으로 봉황대와 연락합시다.물론, 우선 React를 설치해야 합니다.
$ yarn add react react-dom react-router-dom
$ yarn add --dev @types/react @types/react-dom @types/react-router-dom
그런 다음 기본 React 템플릿을 작성할 수 있습니다.자산 폴더에서 app.js
을 app.tsx
으로 이름을 바꾸고 아래와 같이 파일을 다시 작성합니다.assets/js/app.tsx
import '../css/app.css'
import 'phoenix_html'
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import Root from './Root'
// This code starts up the React app when it runs in a browser. It sets up the routing
// configuration and injects the app into a DOM element.
ReactDOM.render(<Root />, document.getElementById('react-app'))
assets/js/Root.tsx
import * as React from 'react'
import { BrowserRouter, Route, Switch } from 'react-router-dom'
import Header from './components/Header'
import HomePage from './pages'
export default class Root extends React.Component {
public render(): JSX.Element {
return (
<>
<Header />
<BrowserRouter>
<Switch>
<Route exact path="/" component={HomePage} />
</Switch>
</BrowserRouter>
</>
)
}
}
assets/js/components/Header.tsx
import * as React from 'react'
const Header: React.FC = () => (
<header>
<section className="container">
<nav role="navigation">
<ul>
<li>
<a href="https://hexdocs.pm/phoenix/overview.html">Get Started</a>
</li>
</ul>
</nav>
<a href="http://phoenixframework.org/" className="phx-logo">
<img src="/images/phoenix.png" alt="Phoenix Framework Logo" />
</a>
</section>
</header>
)
export default Header
assets/js/components/Main.tsx
import * as React from 'react'
const Main: React.FC = ({ children }) => (
<main role="main" className="container">
{children}
</main>
)
export default Main
assets/js/pages/index.tsx
import * as React from 'react'
import { RouteComponentProps } from 'react-router-dom'
import Main from '../components/Main'
const HomePage: React.FC<RouteComponentProps> = () => <Main>HomePage</Main>
export default HomePage
할 수 있을 거예요.현재 프로젝트의
router.ex
폴더를 열고 "/"
범위 내에서 우리의 경로를 수정합니다. 아래와 같습니다.- get "/", PageController, :index
+ get "/*path", PageController, :index
그런 다음 템플릿 파일을 수정하여 React 코드를 올바르게 로드합니다.기본 레이아웃 템플릿에서 스크립트를 사용하여 <body>
표기 내의 모든 내용을 실현할 수 있습니다.templates/layout/app.html.eex
<body>
<%= render @view_module, @view_template, assigns %>
<script type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
</body>
지금은 색인 페이지 템플릿입니다.id
속성을 app.tsx
에 지정된 응용 프로그램 입구점으로 설정해야 합니다.templates/page/index.html.eex
<div id="react-app"></div>
건전성 검사
지금 우리는 모든 것이 정상인지 아닌지를 검사해야 한다.
mix deps.get
과 npm install
을 다시 실행하는 것은 단지 확보하고 mix ecto.setup
을 실행하여 우리의 데이터베이스를 구축하기 위해서이다.그리고 mix phx.server
을 실행하고 Webpack 프로세스가 끝날 때까지 기다린 다음 localhost:4000
으로 이동합니다.만약 그것이 일한다면, 당신은 당신의 웹 페이지가 불러오는 것을 볼 수 있습니다. 축하합니다.우리 하이라이트로 들어가자.
react 라우터를 사용하여 다른 페이지 만들기
현재 우리는 기본적인 Phoenix 서버를 실행하여 React로 할 수 있는 예쁜 일의 예시를 몇 개 만들 수 있습니다.사람들이 React 기능을 보여줄 때 가장 흔히 볼 수 있는 예는 계수기 응용 프로그램이다.
우선, 우리는 카운터 루트를
Root.tsx
파일에 추가할 것이다. import * as React from 'react'
import { BrowserRouter, Route, Switch } from 'react-router-dom'
import Header from './components/Header'
import HomePage from './pages'
+import CounterPage from './pages/counter'
export default class Root extends React.Component {
public render(): JSX.Element {
return (
<>
<Header />
<BrowserRouter>
<Switch>
<Route exact path="/" component={HomePage} />
+ <Route path="/counter" component={CounterPage} />
</Switch>
</BrowserRouter>
</>
)
}
}
그리고 Counter
구성 요소를 추가합니다.assets/js/pages/counter.tsx
import * as React from 'react'
import { Link } from 'react-router-dom'
import Main from '../components/Main'
// Interface for the Counter component state
interface CounterState {
currentCount: number
}
const initialState = { currentCount: 0 }
export default class CounterPage extends React.Component<{}, CounterState> {
constructor(props: {}) {
super(props)
// Set the initial state of the component in a constructor.
this.state = initialState
}
public render(): JSX.Element {
return (
<Main>
<h1>Counter</h1>
<p>The Counter is the simplest example of what you can do with a React component.</p>
<p>
Current count: <strong>{this.state.currentCount}</strong>
</p>
{/* We apply an onClick event to these buttons to their corresponding functions */}
<button className="button" onClick={this.incrementCounter}>
Increment counter
</button>{' '}
<button className="button button-outline" onClick={this.decrementCounter}>
Decrement counter
</button>{' '}
<button className="button button-clear" onClick={this.resetCounter}>
Reset counter
</button>
<br />
<br />
<p>
<Link to="/">Back to home</Link>
</p>
</Main>
)
}
private incrementCounter = () => {
this.setState({
currentCount: this.state.currentCount + 1
})
}
private decrementCounter = () => {
this.setState({
currentCount: this.state.currentCount - 1
})
}
private resetCounter = () => {
this.setState({
currentCount: 0
})
}
}
현재 localhost:4000/counter
으로 이동하여 생성을 테스트합니다.만약 가능하다면, 우리는 다음 부분을 계속할 수 있다.API 가져오기 - 통증 없는 예
앞에서 말한 바와 같이, 내가 인터넷에서 찾은 거의 모든 React + Phoenix 강좌는 React 구성 요소만 과장했다.그들은 지금까지 React와 Phoenix를 어떻게 정확하게 소통할 수 있는지 설명한 적이 없는 것 같다.그게 다 설명됐으면 좋겠어요.
시작하기 전에
router.ex
에 "/api"
성명에 /*path
범위를 성명했는지 확인하십시오.성실히나는 왜 나의 API 루트가 작용하지 않는지 일주일 동안 생각하다가 최근에야 비로소 나의 루트 성명이 상반된다는 것을 깨달았다.router.ex
# ...
scope "/api", PhoenixReactPlaygroundWeb do
pipe_through :api
# ...your API endpoints
end
# ...
scope "/", PhoenixReactPlaygroundWeb do
pipe_through :browser # Use the default browser stack
# This route declaration MUST be below everything else! Else, it will
# override the rest of the routes, even the `/api` routes we've set above.
get "/*path", PageController, :index
end
우리가 그것들을 모두 설정한 후에 우리의 견본 데이터에 새로운 상하문을 만듭니다.$ mix phx.gen.json Example Language languages name:string proverb:string
router.ex
scope "/api", PhoenixReactPlaygroundWeb do
pipe_through :api
+ resources "/languages", LanguageController, except: [:new, :edit]
end
데이터베이스 피드를 만들어서 데이터를 미리 채울 수도 있습니다.이 작업을 수행하는 방법에 대한 자세한 내용은 Elixir Casts course을 참조하십시오.정신 검사를 한 번 더 할 때가 됐어!Phoenix 서버를 실행하고
localhost:4000/api/languages
으로 이동합니다.만약 모든 것이 정상이라면, 데이터베이스를 미리 불러올지 여부에 따라 공백이나 채워진 JSON을 보셔야 합니다.만약 모든 것이 정상이라면, 우리는 지금 우리의 구성 요소를 계속할 수 있다.
Root.tsx
import * as React from 'react'
import { BrowserRouter, Route, Switch } from 'react-router-dom'
import Header from './components/Header'
import HomePage from './pages'
import CounterPage from './pages/counter'
+import FetchDataPage from './pages/fetch-data'
export default class Root extends React.Component {
public render(): JSX.Element {
return (
<>
<Header />
<BrowserRouter>
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/counter" component={CounterPage} />
+ <Route path="/fetch-data" component={FetchDataPage} />
</Switch>
</BrowserRouter>
</>
)
}
}
pages/fetch-data.tsx
import * as React from 'react';
import { Link } from 'react-router-dom';
import Main from '../components/Main';
// The interface for our API response
interface ApiResponse {
data: Language[];
}
// The interface for our Language model.
interface Language {
id: number;
name: string;
proverb: string;
}
interface FetchDataExampleState {
languages: Language[];
loading: boolean;
}
export default class FetchDataPage extends React.Component<
{},
FetchDataExampleState
> {
constructor(props: {}) {
super(props);
this.state = { languages: [], loading: true };
// Get the data from our API.
fetch('/api/languages')
.then(response => response.json() as Promise<ApiResponse>)
.then(data => {
this.setState({ languages: data.data, loading: false });
});
}
private static renderLanguagesTable(languages: Language[]) {
return (
<table>
<thead>
<tr>
<th>Language</th>
<th>Example proverb</th>
</tr>
</thead>
<tbody>
{languages.map(language => (
<tr key={language.id}>
<td>{language.name}</td>
<td>{language.proverb}</td>
</tr>
))}
</tbody>
</table>
);
}
public render(): JSX.Element {
const content = this.state.loading ? (
<p>
<em>Loading...</em>
</p>
) : (
FetchData.renderLanguagesTable(this.state.languages)
);
return (
<Main>
<h1>Fetch Data</h1>
<p>
This component demonstrates fetching data from the Phoenix API
endpoint.
</p>
{content}
<br />
<br />
<p>
<Link to="/">Back to home</Link>
</p>
</Main>
);
}
}
다 좋아요!이제 localhost:4000/fetch-data
으로 넘어가 보겠습니다.결과
만약 당신이 아직도 여기에 있다면, 축하합니다. 당신의 설정이 완성되었습니다!
mix phx.server
을 다시 실행하고 모든 작업을 완료합니다.만약 모든 것이 순조롭다면, 배로 축하해 주십시오!이제 이 지식을 사용하여 다음 React+Phoenix 응용 프로그램을 구축할 수 있습니다.이 안내서의 최종 결과는 here으로 여러분이 시험적으로 사용하실 수 있습니다.
행운을 빕니다.문제가 있으면 언제든지 연락 주십시오.
~selsky 이 문장을 교정하는 데 도움을 주셔서 감사합니다!
Reference
이 문제에 관하여(피닉스와 React: 올바른 길™), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/resir014/phoenix-with-react-the-right-way-25gi텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)