前端工程化系列-构建通用的前端镜像

613 阅读2分钟

目前前端在打包项目时会读取项目目录下的.env文件,将环境变量共同打包进编译文件里。那么这意味着环境变量需要提前先定义才能进行打包操作,而诉求是打包成docker镜像可以到处使用,启动容器时只需要传入环境变量即可,显而易见,现在的做法并不能满足“处处运行”机制,接下来的篇幅会介绍如何达到这个目的。

原理解析

还是将所有的环境变量写入到.env中,不过变量的值是一个带有特殊意义的字符串,也是占位符。

COOKIE_PATH=__PLACEHOLDER_COOKIE_PATH
PUBLIC_PATH=__PLACEHOLDER_PUBLIC_PATH

启动容器时通过启动参数的方式将环境变量传入,通过shell脚本将这些占位符先替换为传入的环境变量,然后在启动nginx服务器。

# 启动容器命令
docker run  -d -p 8080:80 \
fe-project:v1.0.0 \
COOKIE_DOMAIN=.xxx.com \
PUBLIC_PATH=https://aaa.bbb.com

实战

#! /bin/bash

artifactDir=`dirname $0`
placeholderPrefix="__PLACEHOLDER_"
envs=$@

# nuxt子应用打包出来的文件夹
appName=project-public

# 进入当前shell文件的目录
cd $artifactDir

# 如果是nuxt构建出来的子应用,需要将其放置根目录
if [ $IS_SUB = 1 ]; then
    mv ./$appName/* ./
fi

# cut 命令 https://blog.csdn.net/yangshangwei/article/details/52563123
# 可以理解为js的字符串split方法,切割为一个数组
# -d 是自定义分割符
# -f 是指定那个域
function getEnvKey() {
    echo $1 | cut -d '=' -f '1'
}
function getEnvValue() {
    echo $1 | cut -d '=' -f '2-'
}

function isFrontEndFile() {
    local fileSuffix=(
        "css"x
        "js"x
        "html"x
        "htm"x
    )
    for sfx in "${fileSuffix[@]}";
    do
        ## 匹配规则: <https://www.jianshu.com/p/2237f029c385>
        if [ "${1##*.}"x = "${sfx}" ]; then
            return 0
        fi
    done
    return 1
}

# 执行替换操作 https://blog.csdn.net/u010444107/article/details/78849037?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
function doReplace() {
    for env in ${envs[@]};
    do
        envKey=`getEnvKey ${env}`
        envValue=`getEnvValue ${env}`
        echo "执行替换操作: sed -i \\"s#${placeholderPrefix}${envKey}#${envValue}#g\\" ${1}"
        sed -i  "s#${placeholderPrefix}${envKey}#${envValue}#g" $1
    done
}

# 递归遍历
function traverseFile() {
    local files=`ls $1`
    for file in $files;
    do
        filePath=$1/$file
        if [ -d $filePath ]; then
            # 如果是目录就递归调用
            traverseFile $filePath
        else
            # 检测是不是前端文件
            if isFrontEndFile $filePath; then
                # echo "${filePath}是前端文件"
                doReplace $filePath
            fi
        fi
    done
}

echo "环境变量${envs}"
echo "执行替换环境变量"
echo "${artifactDir}"
traverseFile $artifactDir

echo "启动nginx"
nginx -g "daemon off;"

FROM nginx:1.16.1-alpine
USER root
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN apk update && apk upgrade && apk add bash && apk add bash-doc && apk add bash-completion && bash
COPY ./config/nginx/nginx.default.conf /etc/nginx/conf.d/default.conf
COPY dist/ /app
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod -R 777 /app/entrypoint.sh
WORKDIR /app
ENTRYPOINT ["./entrypoint.sh"]

server {
  listen 80;
  server_name localhost;

  # 开启GZIP
  gzip on;
  gzip_buffers 32 4K;
  gzip_comp_level 6;
  gzip_min_length 100;
  gzip_types application/javascript text/css text/xml;
  gzip_vary on;

  location / {
    root /app;
    index index.html index.htm;
  }
}