如何優雅的打包前端程式碼

李銘昕發表於2019-10-30

我就是頭髮掉光,累死,死在ICU,也不會使用 Docker

唉呀媽呀,真香。。。

Hyperf In Docker

作為 Hyperf 框架作者之一,強烈安利大家使用 Docker,現在 Docker叢集 技術已經十分成熟,K8s 強勢領跑,深受一線企業的厚愛,而Swarm 使用簡單,絕對是中小型企業的首選。

但今天,並不是講 HyperfDocker 中的應用,而是 前端 如何在 Docker 中進行打包,並與 Hyperf 進行通訊。

打包

我們下面以 VUE 為例。這裡提供一個倉庫 vue-docker-demo,供大家測試。

首先我們使用 vue 腳手架初始化一個專案。

vue create vue-docker-demo

接下來我們初始化 Hyperf 專案,這裡為了方便講解,後端專案也一同上傳到這一個倉庫中。

cd vue-docker-demo
composer create hyperf/biz-skeleton hyperf

修改 Hyperf 專案,方便測試

新建 UserController 控制器

<?php

declare(strict_types=1);

namespace App\Controller;

class UserController extends Controller
{
    public function info(int $id)
    {
        return $this->response->success([
            'id' => $id,
            'name' => 'Hyperf',
        ]);
    }

    public function update(int $id)
    {
        $name = $this->request->input('name');

        return $this->response->success([
            'id' => $id,
            'name' => $name,
        ]);
    }
}

新增路由

<?php

Router::get('/user/{id:\d+}', 'App\Controller\UserController@info');
Router::post('/user/{id:\d+}', 'App\Controller\UserController@update');

新增單元測試

<?php

declare(strict_types=1);

namespace HyperfTest\Cases;

use HyperfTest\HttpTestCase;

/**
 * @internal
 * @coversNothing
 */
class UserTest extends HttpTestCase
{
    public function testUserInfo()
    {
        $res = $this->get('/user/1');

        $this->assertSame(0, $res['code']);
        $this->assertSame(['id' => 1, 'name' => 'Hyperf'], $res['data']);
    }

    public function testUserUpdate()
    {
        $res = $this->json('/user/1', [
            'name' => 'limx',
        ]);

        $this->assertSame(0, $res['code']);
        $this->assertSame(['id' => 1, 'name' => 'limx'], $res['data']);
    }
}

跑一下介面測試

$ composer test
> co-phpunit -c phpunit.xml --colors=always
Detected an available cache, skip the app scan process.
Detected an available cache, skip the vendor scan process.
[DEBUG] Event Hyperf\Framework\Event\BootApplication handled by Hyperf\Di\Listener\BootApplicationListener listener.
[DEBUG] Event Hyperf\Framework\Event\BootApplication handled by Hyperf\Config\Listener\RegisterPropertyHandlerListener listener.
[DEBUG] Event Hyperf\Framework\Event\BootApplication handled by Hyperf\Paginator\Listener\PageResolverListener listener.
PHPUnit 7.5.16 by Sebastian Bergmann and contributors.

...                                                                 3 / 3 (100%)

Time: 309 ms, Memory: 16.00 MB

OK (3 tests, 14 assertions)

改造 VUE 專案

我前端水平有限,所以就寫點簡單的測試,主要是為了試驗 Dockerfile

使用 NPM 安裝 axios

npm i axios -S

新增 request.js

import axios from 'axios'

export default {
    async request(method, url, params) {
        const BASE_URI = '/api';

        return axios({
            method: method,
            url: `${BASE_URI}${url}`,
            data: params,
        });
    }
}

修改 HelloWorld.vue,以下只展示修改後的部分

<script>
    import request from "../api/request";

    export default {
        name: 'HelloWorld',
        messaage: '',
        props: {
            msg: String
        },
        async mounted() {
            var data = await request.request('GET', '/user/1');
            // eslint-disable-next-line no-console
            console.log(data);

            var res = await request.request('POST', '/user/1', { name: "limx" });
            // eslint-disable-next-line no-console
            console.log(res);
        }
    }
</script>

新增 Dockerfile 和 app.conf

首先,當 nginx 拿到 /api 字首後,轉發到對應的後端服務,所以這裡需要提供一份 app.conf 配置

server {
    listen  80;
    root    /usr/src/app/dist;
    index   index.html;
    client_max_body_size 8M;

    proxy_set_header    Host                $host:$server_port;
    proxy_set_header    X-Real-IP           $remote_addr;
    proxy_set_header    X-Real-PORT         $remote_port;
    proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;

    location /api/ {
        proxy_pass  http://biz-skeleton:9501/;
    }
}

接下來是我們的 Dockerfile,邏輯其實很簡單,我們先透過 node 環境進行打包,然後再 copynginx 環境下即可。

FROM node:10-alpine as builder

WORKDIR /usr/src/build

ADD package.json /usr/src/build
ADD package-lock.json /usr/src/build
RUN npm install -g cnpm --registry=https://registry.npm.taobao.org && cnpm install

COPY . /usr/src/build
RUN npm run-script build

FROM nginx:alpine

COPY --from=builder /usr/src/build/dist /usr/src/app/dist
COPY --from=builder /usr/src/build/app.conf /etc/nginx/conf.d/

ENTRYPOINT ["nginx", "-g", "daemon off;"]

打包測試

首先進入我們 Hyperf 目錄,打包後端服務

cd hyperf
docker build . -t biz-skeleton:latest

然後打包我們的前端程式碼

docker build . -t vue-demo

建立閘道器,如果已經建立過,可以忽略這裡,並使用建立過的閘道器

$ docker network create \
--subnet 10.0.0.0/24 \
--opt encrypted \
--attachable \
default-network

接下來,讓我們把兩個專案都跑起來

docker run -p 9501:9501 --name biz-skeleton --network default-network --rm -d biz-skeleton:latest
docker run -p 8080:80 --name vue-demo --network default-network --rm -d vue-demo:latest

然後透過瀏覽器訪問 http://127.0.0.1:8080/

就可以看到我們的測試結果在終端中輸出了。

釋出

專案釋出這裡就不再贅述了,需要了解的就去看一下 DockerSwarm 叢集搭建,有全自動的打包釋出方案,教程就在 Hyperf 官方文件中。

這裡需要額外提一下的是,打包好的靜態檔案,每次都走伺服器公網流量是很浪費的,這裡推薦大家使用 CDN,然後配一個回源,可以大大減少流量的壓力。當然,回源策略那裡要注意一下,把介面返回的資料也快取到 CDN 上就不好了。

寫在最後

Hyperf 是基於 Swoole 4.4+ 實現的高效能、高靈活性的 PHP 協程框架,內建協程伺服器及大量常用的元件,效能較傳統基於 PHP-FPM 的框架有質的提升,提供超高效能的同時,也保持著極其靈活的可擴充套件性,標準元件均基於 PSR 標準 實現,基於強大的依賴注入設計,保證了絕大部分元件或類都是 可替換 與 可複用 的。

框架元件庫除了常見的協程版的 MySQL 客戶端、Redis 客戶端,還為您準備了協程版的 Eloquent ORM、WebSocket 服務端及客戶端、JSON RPC 服務端及客戶端、GRPC 服務端及客戶端、Zipkin/Jaeger (OpenTracing) 客戶端、Guzzle HTTP 客戶端、Elasticsearch 客戶端、Consul 客戶端、ETCD 客戶端、AMQP 元件、Apollo 配置中心、阿里雲 ACM 應用配置管理、ETCD 配置中心、基於令牌桶演算法的限流器、通用連線池、熔斷器、Swagger 文件生成、Swoole Tracker、Blade 和 Smarty 檢視引擎、Snowflake 全域性ID生成器 等元件,省去了自己實現對應協程版本的麻煩。

Hyperf 還提供了 基於 PSR-11 的依賴注入容器、註解、AOP 面向切面程式設計、基於 PSR-15 的中介軟體、自定義程式、基於 PSR-14 的事件管理器、Redis/RabbitMQ 訊息佇列、自動模型快取、基於 PSR-16 的快取、Crontab 秒級定時任務、Translation 國際化、Validation 驗證器 等非常便捷的功能,滿足豐富的技術場景和業務場景,開箱即用。

本作品採用《CC 協議》,轉載必須註明作者和本文連結
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

相關文章