Let’s Build |> 使用Elixir,Phoenix和React打造克隆版的Slack(part 1)

Let’s Build |> 使用Elixir,Phoenix和React打造克隆版的Slack(part 1 — Project setup)

Live Demo---GitHub Repojavascript

緣起

我是一個Ruby程序員,最近開始學習Elixir。我驚歎於Elixir和Phoenix展示的技術魅力,並很快喜歡上了這個新東西。就像Rails之於Ruby同樣,Phoenix使得Elixir變得流行起來,緣由就在於Elixir使得開發人員可以高效的編寫出性能優秀,穩定性好的應用程序,而且很容易使用這些應用處理實時數據。html

寫這篇博文時,我只有大約一週的Phoenix使用經驗。我寫這篇博文的目的就是趨勢本身從不一樣的角度思考正在解決的問題,以期得到對這門語言和框架更深刻的理解。若是你在代碼中發現任何錯誤或有改進的建議,歡迎給我來信或者提交pull request!前端

若是你和我同樣也是個Elixir新手,我推薦你閱讀 Programming ElixirProgramming Phoenix ,這兩本書全面的闡述了Elixir和Phoenix的基本要點。java

咱們要作什麼

爲了向我喜好的聊天應用Slack致敬,我將打造一個高仿版的Slack,我叫它Sling。爲了讓這篇博文簡潔、完整、可讀,我只實現Slack的部分功能,可是要實現的這部分功能足夠咱們習得Phoenix的基本原理。react

Slack有team的概念,而且每一個team有若干個channel。team成員可以加入到channel,channel就是聊天的地方。簡化起見,咱們就不建立team這個功能了,取而代之的是咱們將建立room,每一個註冊用戶都能加入到room中,而且在room中聊天。git

咱們會使用牛x的Phoenix Presence Module展現當前room中的在線用戶。程序員

我將盡量詳盡的展現出我是如何構建這個應用的,爲此我會保持小增量的git commit。而且在每次提交後留下git diff的連接。github

查看當前代碼web

技術棧

服務器端sql

前端

因爲個人技術經驗是使用Ruby構建web應用,因此熟悉Rails的讀者對於我寫的東西會更易於理解。我假定你熟悉JavaScriptES6。因爲這不是React 教程,因此我會盡可能解釋React組件相關的邏輯,可是不會深究。

若是你尚未安裝Elixir或者Phoenix請看這裏

項目結構

對於咱們要構建的應用而言,真實項目通常會建立兩個獨立的代碼倉庫,一個用於放置Phoenix API,另外一個用於放置React App。可是爲了使咱們的博文清楚明瞭,我將代碼放置在同一個倉庫中。目錄結構以下:

sling/
    |--- api/
        (phoenix app)
    |--- web/
        (react app)

開始吧!少年。

建立Phoenix應用

建立一個新的文件夾做爲咱們的代碼倉庫

mkdir sling
cd sling

生成新的Phoenix應用,咱們使用Phoenix直接做爲JSON API。因此不須要默認安裝的asset manager, 使用參數--no-brunch;也不須要html模板和瀏覽器端的路由,使用參數--no-html

mix phoenix.new sling --no-html --no-brunch
mv sling api

建立React應用

使用 create-react-app初始化React App,這是一個強大的工具,零配置搭建咱們的前端應用。

安裝 create-react-app命令行工具

npm i -g create-react-app

建立React App

create-react-app sling
mv sling web

牛叉吧,咱們已經初始化好了後端的Phoenix API和前端的React App。

咱們第一個正式的提交 init commit

配置Phoenix項目

首先配置數據庫,開發環境下默認的數據庫配置位於該文件sling/api/config/dev.exs, PostgreSQL默認用戶密碼均爲 postgres 。安全起見咱們新建一個文件dev.secret.exs,用於存放私人的數據庫配置信息,覆蓋掉默認的數據庫鏈接配置。這樣一來也便於別人使用咱們的代碼。將dev.secret.exs加入到.gitignore中(因爲新建的配置文件是私有信息因此沒必要提交),內容以下:

sling/api/config/dev.secret.exs

use Mix.Config

config :sling, Sling.Repo,
  username: "your_postgres_user",
  password: "your_postgres_password"

dev.exs的末尾添加 import "dev.secret.exs",這樣咱們的私有配置才能生效。

sling/api/config/dev.exs

# contents above

import_config "dev.secret.exs"

建立數據庫(當前所在路徑爲sling/api

mix ecto.create

數據庫建立完成後,啓動Phoenix App 。

mix phoenix.server

訪問 http://localhost:4000, 正常狀況會報錯,緣由在於咱們的Phoenix App 只用作API,沒有配置網頁瀏覽相關的路由。

Git Commit

Phoenix已經配置完成,接下來咱們配置 React App。

配置React項目

create-react-app已近初始化了一個可運行的app, npm start, 訪問 http://localhost:3000,就能看到初始化的react app。咱們要配置本身的redux react-router, 因此刪除掉web/src目錄下的全部文件。

另,咱們將使用最新的JavaScript依賴管理工具Yarn, 安裝指南在此.

在這個前端應用中有許多第三方庫咱們須要使用,一次性所有將其安裝(當前目錄是sling/web

yarn add aphrodite lodash md5 moment phoenix react-redux react-router@4.0.0-alpha.5 redux redux-form redux-thunk

你應該注意到咱們使用v4-alpha版本的react-router, 其相較於v2版本的react-router有不少重大的改變。借這個機會咱們一併學一學v4-alpha版react-router, 期待v4正式版儘快發佈,若有變化到時我會更新博文。

我將使用Airbnb's styleguide,其中用到了eslint和flow,因此接下來咱們安裝開發環境下用到的第三方庫。

yarn add babel-eslint eslint eslint-config-airbnb eslint-plugin-flowtype eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react --dev

Linting rules的配置徹底依賴於我的的喜愛,下面是我本身用的.eslintrc文件。(react/no-unused-prop-types 規則被disable掉了,緣由是和flowtype有衝突)

sling/web/.eslintrc

{
  "parser": "babel-eslint",
  "plugins": ["react", "flowtype"],
  "extends": ["airbnb", "plugin:flowtype/recommended"],
  "rules": {
    "react/jsx-filename-extension": 0,
    "import/prefer-default-export": 0,
    "react/no-unused-prop-types": 0,
    "camelcase": 0
  },
  "globals": {
    "fetch": true,
    "window": true,
    "document": true,
    "localStorage": true
  }
}

提交代碼 show installed packages

配置React/Redux

React項目有各類各樣的目錄結構,固然都是基於應用場景權衡的結果。就咱們的項目而言,建立containers目錄用於存放和redux store 鏈接相關的組件。建立components目錄,存放其餘組件。建立actions和reducers目錄分別用於存放action和reducer相關的文件。建立store目錄存放redux store相關的配置文件。咱們着手開始吧。

建立app的入口文件 sling/web/src/index.js, 這個文件須要導入redux store配置文件(稍後建立), App容器組件,並掛載到 index.html 的<div id="root" />節點下。

sling/web/src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import App from './containers/App';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>
  , document.getElementById('root')
);

建立上面提到的redux store配置文件,引入reducers文件(稍後建立),使用redux-thunk 處理異步操做和Promises。

sling/web/src/store/index.js

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from '../reducers';

const middleWare = [thunk];

const createStoreWithMiddleware = applyMiddleware(...middleWare)(createStore);

const store = createStoreWithMiddleware(reducers);

export default store;

建立reducer根文件sling/web/src/reducers/index.js,這個文件用於彙總其餘的reducer文件,可是如今咱們只須要用redux-form使reducer 能正常工做便可。咱們不直接返回配置參數的combineReducers函數,相反,當logout時咱們強制返回帶undefined參數的appReducer,這樣就會強制初始化全部reducer的state(也就是強制清理登出用戶的redux state,不會讓其污染下一個login用戶的state)

sling/web/src/reducers/index.js

import { combineReducers } from 'redux';
import { reducer as form } from 'redux-form';

const appReducer = combineReducers({
  form,
});

export default function (state, action) {
  if (action.type === 'LOGOUT') {
    return appReducer(undefined, action);
  }
  return appReducer(state, action);
}

如今咱們來建立App組件,在這個組件中咱們要到了v4版的react-router 來配置頁面路由。目前咱們只有兩個路由,一個是Home路由,另外一個是404頁面。

sling/web/src/containers/App/index.js

// @flow
import React, { Component } from 'react';
import { BrowserRouter, Match, Miss } from 'react-router';
import Home from '../Home';
import NotFound from '../../components/NotFound';

class App extends Component {
  render() {
    return (
      <BrowserRouter>
        <div>
          <Match exactly pattern="/" component={Home} />
          <Miss component={NotFound} />
        </div>
      </BrowserRouter>
    );
  }
}

export default App;

目前咱們的Home頁面只是簡單的組件

sling/web/src/containers/Home/index.js

// @flow
import React from 'react';

const Home = () => (<div>Home</div>);

export default Home;

NotFound組件

sling/web/src/components/NotFound/index.js

// @flow
import React from 'react';
import { Link } from 'react-router';

const NotFound = () =>
  <div style={{ margin: '2rem auto', textAlign: 'center' }}>
    <p>Page not found</p>
    <p><Link to="/">Go to the home page →</Link></p>
  </div>;

export default NotFound;

好,redux的基本配置已經完成。

查看commit的代碼

到目前爲止,咱們的前端App和後端API還沒法通信,不過也好,本篇博文就此結束。下篇博文咱們將實現前端和後端的通信,而且添加用戶帳戶和用戶身份認證。

首發地址:http://blog.zhulinpinyu.com/2017/06/22/lets-build-a-slack-clone-with-elixir-phoenix-and-react-part-1-project-setup/

相關文章
相關標籤/搜索