この記事は、ほとんどすべてのWebアプリケーション開発者が直面しているいくつかのトピック、つまり静的ファイルの操作、構成、サーバーへのアプリケーションの配信を小さなガイドにまとめようとする著者の試みです。 この記事の執筆時点で、Phoenix Frameworkの最後の安定ブランチは1.2.xでした。
ブランチしない理由と、移行を蒸留酒製造業者と組み合わせる方法を誰が気にしますか?
PhoenixはデフォルトでBrunchを使用してJSコードとアセットを処理します-おそらく非常にシンプルで高速なバンドル機能ですが、エコシステムの機能とサイズの点ではもちろん、最も普及しておらず、最も強力でもありません(もちろんStackOverflowへの回答)。 そのため、ブランチは事実上Webpackに置き換えられました。 これは、現在のフロントエンドの世界で一番のバンドラーです。
しかし、展開の問題を解決するために、フレームワークは、さまざまな環境のさまざまな構成を囲む機能以外のほとんど何も提供しません。 フォーラムやその他のプラットフォーム上のさまざまな開発者の回答から判断すると、多くの開発者は、開発ツールをバトルサーバーに直接インストールし、Mixを使用してアプリケーションをコンパイルおよび起動することにより、アプリケーションをデプロイします。 いくつかの理由から、このアプローチは受け入れられないと考えているため、アプリケーションを自己完結型のパッケージにパックするためのいくつかのオプションを試して、Distilleryに決めました。
なぜなら 記事がチュートリアルの場合、例として、特定のユーザーのリストを表示する絶対に不要なアプリケーションが開発されます。 すべてのコードはGitHubで利用できます 。各ステップは個別のコミットとして修正されるため、変更履歴を確認することをお勧めします。 また、特定のステップでコミットへのリンクを提供します。これにより、一方ではdiffからどの変更が行われたかが明確に表示され、もう一方ではリストでテキストが乱雑にならないようにします。
準備する
したがって、プロジェクトのテンプレートを作成し、ブランチを使用しないことを示します。
$ mix phoenix.new userlist --no-brunch
$ mix ecto.create && mix ecto.migrate
$ mix phoenix.gen.model User users name active:boolean
, - , "" priv/repo/seeds.exs, . :
$ mix ecto.migrate && mix run priv/repo/seeds.exs
priv/repo/migrations/ – , , http API, JSON- (). , diff , , , , "http-" /api/users, JSON .
,
$ mix phoenix.server
, , .
JS
, , – priv/static/ web/static/. , "Hello, World!" , , , . , web/static/, - , Brunch ( ), , priv/static, ( ).
, , priv/static/, Brunch' , web/static/, , web/static/assets/, … , - , , :
- priv/static/ , - . , VCS .gitignore;
- web/static/ , /priv/static , ..
- , priv/static/ (js, ) - .
, priv/static , robots.txt favicon.ico web/static/ – . , html ().
, Webpack, NPM:
$ npm init
{
"name": "userlist",
"version": "1.0.0",
"description": "Phoenix example application",
"scripts": {
},
"license": "MIT"
}
$ npm install --save-dev webpack
- JS , , :
console.log("App js loaded.");
JS- web/js/, app.js . web/templates/layout/app.html.eex, </body>:
<script src="<%= static_path(@conn, "/js/app.js") %>"></script>
static_path, digest-, , .
Webpack' – webpack.config.js :
module.exports = {
entry: __dirname + "/web/js/app.js",
output: {
path: __dirname + "/priv/static",
filename: "js/app.js"
}
};
, app.js priv/static/js/ . Webpack , , , . , , watch scripts package.json:
"scripts": {
"watch": "webpack --watch-stdin --progress --color"
},
Webpack
$ npm run watch
, Phoenix, , watchers, . config/dev.exs, npm:
watchers: [npm: ["run", "watch"]]
, Webpack
$ mix phoenix.server
C JS , web/static/. Webpack, copy:
$ npm install --save-dev copy-webpack-plugin
var CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = {
entry: __dirname + "/web/js/app.js",
output: {
path: __dirname + "/priv/static",
filename: "js/app.js"
},
plugins: [
new CopyWebpackPlugin([{ from: __dirname + "/web/static" }])
]
};
, priv/static/ – JS , . , JS (), () - ().
– , -, , CSS. – CSS , Webpack , JS. css-loader', .
. Distillery.
Distillery Exrm Elixir. , , Distillery . , mix.exs:
{:distillery, "~> 1.4"}
$ mix deps.get && mix release.init
rel/config.exs :
Path.join(["rel", "plugins", "*.exs"])
|> Path.wildcard()
|> Enum.map(&Code.eval_file(&1))
use Mix.Releases.Config,
# This sets the default release built by `mix release`
default_release: :default,
# This sets the default environment used by `mix release`
default_environment: Mix.env()
environment :dev do
set dev_mode: true
set include_erts: false
set cookie: :"Mp@oK==RSu$@QW.`F9(oYks&xDCzAWCpS*?jkSC?Zo{p5m9Qq!pKD8!;Cl~gTC?k"
end
environment :prod do
set include_erts: true
set include_src: false
set cookie: :"/s[5Vq9hW(*IA>grelN4p*NjBHTH~[gfl;vD;:kc}qAShL$MtAI1es!VzyYFcC%p"
end
release :userlist do
set version: current_version(:userlist)
set applications: [
:runtime_tools
]
end
, . : :userlist, :default, .. , :dev :prod. OTP Release – , , ERTS. , :userlist, . , .
. , config/prod.secret.exs, . VCS, , , :
use Mix.Config
config :userlist, Userlist.Endpoint,
secret_key_base: "uE1oi7t7E/mH1OWo/vpYf0JLqwnBa7bTztVPZvEarv9VTbPMALRnqXKykzaESfMo"
# Configure your database
config :userlist, Userlist.Repo,
adapter: Ecto.Adapters.Postgres,
username: "phoenix",
password: "",
database: "userlist_prod",
pool_size: 20
Userlist.Endpoint config/prod.exs. , , 80 PORT server, , Cowboy:
url: [host: "localhost", port: {:system, "PORT"}],
...
server: true
, Babel JS , .. UglifyJS, - Webpack, ES6:
$ npm install --save-dev babel-loader babel-core babel-preset-es2015
Babel webpack.config.js plugins:
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
query: {
presets: ["es2015"]
}
}
]
}
"scripts": {
"watch": "webpack --watch-stdin --progress --color",
"deploy": "webpack -p"
},
:
$ npm run deploy $ MIX_ENV=prod mix phoenix.digest $ MIX_ENV=prod mix release $ PORT=8080 _build/prod/rel/userlist/bin/userlist console
JS ( ..), static-; ; . – , .
_build (exploded) , _build/prod/rel/userlist/releases/0.0.1/userlist.tar.gz.
, , command:
command <mod> <fun> [<args..>] # execute the given MFA
rpc, , command – . , , . lib/userlist/release_tasks.ex ():
defmodule Release.Tasks do
alias Userlist.Repo
def migrate do
Application.load(:userlist)
{:ok, _} = Application.ensure_all_started(:ecto)
{:ok, _} = Repo.__adapter__.ensure_all_started(Repo, :temporary)
{:ok, _} = Repo.start_link(pool_size: 1)
path = Application.app_dir(:userlist, "priv/repo/migrations")
Ecto.Migrator.run(Repo, path, :up, all: true)
:init.stop()
end
end
, , , – , Ecto. , , ( Elixir, .. ):
$ MIX_ENV=prod mix release
:
$ _build/prod/rel/userlist/bin/userlist command 'Elixir.Release.Tasks' migrate
:
$ PORT=8080 _build/prod/rel/userlist/bin/userlist console
, , , . , , , , . Distillery ( ).
Distillery
– shell-, (), () boot-. : pre/post_start pre/post_stop.
, , , , :
$ _build/prod/rel/userlist/bin/userlist migrate
manifest.json
, phoenix.digest, - ( ), , priv/static/manifest.json, . , :
, lib/userlist/endpoint.ex:
only: ~w(css fonts images js favicon.ico robots.txt manifest.json)
, Ajax', ;
, ( , , ), LayoutView :
defmodule Userlist.LayoutView do use Userlist.Web, :view def digest do manifest = Application.get_env(:userlist, Userlist.Endpoint, %{})[:cache_static_manifest] || "priv/static/manifest.json" manifest_file = Application.app_dir(:userlist, manifest) if File.exists?(manifest_file) do manifest_file |> File.read! else %{} end end end
, - , :
<script> var digest = <%= raw digest() %> </script>
systemd
, , . , systemd, init- , , .
, /opt/userlist/ userlist. userlist.service ():
# Userlis is a Phoenix, Webpack and Distillery demo application
[Unit]
Description=Userlist application
After=network.target
[Service]
Type=simple
User=userlist
RemainAfterExit=yes
Environment=PORT=8080
WorkingDirectory=/opt/userlist
ExecStart=/opt/userlist/bin/userlist start
ExecStop=/opt/userlist/bin/userlist stop
Restart=on-failure
TimeoutSec=300
[Install]
WantedBy=multi-user.target
, , , /etc/systemd/system/:
$ sudo cp userlist.service /etc/systemd/system
"":
$ sudo systemctl enable userlist.service
:
$ sudo systemctl start userlist
, Phoenix' - - , . , , , , ..
, , , , .