面试、简历上的项目没有亮点怎么办?那就造一个呗

很多公司陆陆续续都开始了秋招又或者说刚毕业还没有找到工作,相信很多朋友都在为简历发愁,想到在面试的时候肯定会有被面试官问项目亮点的场景并说不出所以然,在日常的学习或者工作中都是一些 CRUD,所以就算项目做了但是很多,但是最终看起来还是普普通通,没有什么特别亮的点。

那么接下来我就分享一下我最近在做的这几个开源项目吧,来分享一下我的观点,希望对你有帮助。

Create—Neat 前端脚手架

脚手架我们在项目开发中基本都会用到了,vue 这边的话很多人用的可能就是 create-vite,react 这边的话现在很多开发者都是在用 next 或者在学 next 的路上,所以自然也是 create-next-app 了。

这些项目虽然工程化已经帮我们做好了,项目也基本能使用了,但是还不是说几乎完美的,还是有很多优化或者说可以新增的点的。

最近都是在重构这个项目,而我主要做的事情主要还是在工程化这方面的事情吧。作为一个 ****** 的开源项目,那它肯定离开不了 ****** action 还有 husky,要从这两个点来做切入的话,那能做的亮点可就多咯。

目前我能随口讲出来的主要有以下这两点:

  1. 利用增量 CI 方法,减少 GitHub Actions 运行过程中的时间消耗。
  2. 通过 Husky 编写 Shell 脚本,规范提交信息和分支管理,确保历史记录清晰。

接下来我们就来看看它具体是如何实现的。

增量 CI

增量 CI 的话,最简单的方案还是通过 git diff 的方式来获取到当前提交所变更的文件,只需要检测出文件,拿到文件路径,将其交给 eslintprettier 等工具做检验即可,这样我们就避免了全量检查带来的时间损耗,如果项目足够大的话,那效果是非常可观的,具体实现如下文章所示:

如果你学过 pnpm 的话,它安装依赖包的方式也是有使用到增量的思想,例如我有一个 npm 包,我首先安装了一个版本,它会被缓存到本地,然后我这个包的版本升级了一个版本,那么它是会另外存放变更的文件的,最终通过两者合并的方式,既保留了之前的版本,也可以保存了新的版本。

编写 shell 脚本

不仅是编写 shell 脚本,这个思路用 node 脚本也是可以的,只要你能让他在 husky 中的 commit-msg 钩子中执行该文件就行了,它的主要思想还是使用到 git 命令,来获取到它的详细提交信息,如果前缀不带 feat|fix 会直接拒绝掉提交的,通过这种方式也是实现了规范提交信息和分支管理,确保历史记录清晰。

#!/bin/sh

# 获取两个参数:起始SHA和结束SHA
start_sha=$1
end_sha=$2

# 设置颜色变量
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 定义提交信息规范函数
check_commit_message() {
    commit_msg="$1"
    # 检查提交信息是否以指定的前缀开头
    if ! echo "$commit_msg" | grep -qE "^(feat|fix|docs|style|refactor|test|chore|ci):"; then
        echo -e "${RED}Inappropriate commit message:" "${NC}$1" >&2
        echo -e "${RED}Error:${NC} Commit message format is incorrect. It should start with one of '${BLUE}feat|fix|docs|style|refactor|test|chore|ci:${NC}'." >&2
        exit 1
    fi
}

# workflows传入两个参数,遍历从start_sha到end_sha的所有提交
 if [ $# -eq 2 ]; then
for sha in $(git rev-list $start_sha..$end_sha); do
    commit_msg=$(git show --format=%B -s $sha)
    check_commit_message "$commit_msg"
done
# huksy触发commit-msg钩子时传入一个参数
elif [ $# -eq 1 ]; then
   check_commit_message "$(cat $1) "
else
   echo -e "${RED} error: Failed to get commit message\n"
fi

echo -e "${BLUE}Commit message check passed.${NC}\n"

这是我们所要编写的 shell 脚本,如果要更严格的话,还可以去获取到它的分支规不规范,正常来说一个分支名应该是这样的格式的 feat/login,feat 代表的是新增功能,login 是代表了新增的功能是 login。

最后更多详细的亮点正等待着你的发掘:

create-ai-tool

这个项目是一个 ai 工具吧,主要是方便我们在开发过程中,通过结合 gpt 的 key 来做一些项目开发商的一些创新吧。它主要能聊的点有以下几个方面:

  1. 根据暂存区的修改内容自动生成 commit 信息。
  2. 根据暂存区的修改内容自动生成 code review 的结果,提供一些修改的建议。
  3. 根据 swagger 文档自动为我们前端项目生成封装好的接口和 ts 类型。

前面两个点就是读取 git 上暂存区的内容,将内容作为 prompt 传递给 gpt,并添加一些额外的 prompt,让他输出固定的格式,自动生成 commit 信息和 code review 都是这个思路。

第三个的话就有所不同了,首先我要在我前端的项目上编写好一个已经封装好的 fetch 请求或者 axios 请求,假设我的 fetch 封装如下所示:

type Method = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";

interface Params {
  cacheTime?: number; // 缓存时间,单位为秒。默认强缓存,0为不缓存
  params?: Record<string, any>;
}

interface Props extends Params {
  url: string;
  method: Method;
  mode?: RequestMode; // 添加 mode 属性
  token?: string; // 添加 token 属性
}

type Config =
  | { next: { revalidate: number } }
  | { cache: "no-store" }
  | { cache: "force-cache" };

class Request {
  baseURL: string;

  constructor(baseURL: string) {
    this.baseURL = baseURL;
  }

  /**
   * 请求拦截器
   */
  interceptorsRequest({ url, method, params, cacheTime, mode, token }: Props) {
    let queryParams = ""; // url参数
    let requestPayload = ""; // 请求体数据

    // 请求头
    const headers: Record<string, string> = {};
    if (token) {
      headers["Authorization"] = `Bearer ${token}`;
    }

    const config: Config =
      cacheTime !== undefined
        ? cacheTime > 0
          ? { next: { revalidate: cacheTime } }
          : { cache: "no-store" }
        : { cache: "force-cache" };

    if (method === "GET" || method === "DELETE") {
      // fetch 对 GET 请求等,不支持将参数传在 body 上,只能拼接 url
      if (params) {
        queryParams = new URLSearchParams(params).toString();
        url = `${url}?${queryParams}`;
      }
    } else {
      // 非 form-data 传输 JSON 数据格式
      if (
        !["[object FormData]", "[object URLSearchParams]"].includes(
          Object.prototype.toString.call(params)
        )
      ) {
        headers["Content-Type"] = "application/json";
        requestPayload = JSON.stringify(params);
      }
    }

    return {
      url,
      options: {
        method,
        headers,
        mode, // 添加 mode 属性
        body:
          method !== "GET" && method !== "DELETE" ? requestPayload : undefined,
        ...config,
      },
    };
  }

  /**
   * 响应拦截器
   */
  interceptorsResponse<T>(res: Response): Promise<T> {
    return new Promise((resolve, reject) => {
      const requestUrl = res.url;
      if (res.ok) {
        resolve(res.json() as Promise<T>);
      } else {
        res
          .clone()
          .text()
          .then((text) => {
            try {
              const errorData = JSON.parse(text);
              reject({ message: errorData || "接口错误", url: requestUrl });
            } catch {
              reject({ message: text, url: requestUrl });
            }
          });
      }
    });
  }

  async httpFactory<T>({
    url = "",
    params = {},
    method,
    mode,
    token,
  }: Props): Promise<T> {
    const req = this.interceptorsRequest({
      url: this.baseURL + url,
      method,
      params: params.params,
      cacheTime: params.cacheTime,
      mode,
      token,
    });

    const res = await fetch(req.url, req.options);
    return this.interceptorsResponse<T>(res);
  }

  async request<T>(
    method: Method,
    url: string,
    params?: Params,
    mode?: RequestMode,
    token?: string
  ): Promise<T> {
    return this.httpFactory<T>({ url, params, method, mode, token });
  }

  get<T>(
    url: string,
    params?: Params,
    mode?: RequestMode,
    token?: string
  ): Promise<T> {
    return this.request("GET", url, params, mode, token);
  }

  post<T>(
    url: string,
    params?: Params,
    mode?: RequestMode,
    token?: string
  ): Promise<T> {
    return this.request("POST", url, params, mode, token);
  }

  put<T>(
    url: string,
    params?: Params,
    mode?: RequestMode,
    token?: string
  ): Promise<T> {
    return this.request("PUT", url, params, mode, token);
  }

  delete<T>(
    url: string,
    params?: Params,
    mode?: RequestMode,
    token?: string
  ): Promise<T> {
    return this.request("DELETE", url, params, mode, token);
  }

  patch<T>(
    url: string,
    params?: Params,
    mode?: RequestMode,
    token?: string
  ): Promise<T> {
    return this.request("PATCH", url, params, mode, token);
  }
}

// 接口地址
const request = new Request("https://******.com/xun082/create-neat");

export default request;

export interface ApiResponse<T> {
  data: T;
  message: string;
  reason: string;
}

export const handleRequest = async <T>(
  requestFn: () => Promise<T>
): Promise<T> => {
  try {
    return await requestFn();
  } catch (error) {
    console.error("Error processing request:", error);
    throw error;
  }
};

将这段代码作为 prompt 传递给 gpt,让他在后续的内容中都是以这个作为封装,最后通过网络请求的方式,获取到 swagger 文档上的 json 内容,这个前提是后端已经把 json 的内容写得够详细, 一些需要传的参数以及返回的数据是怎么样的格式的。

得到这个 json 的内容之后传递给 gpt,然后让具体返回具体的内容,通过这种方式,可以让我们减轻了封装接口的时间。

最后该项目还有很多亮点等待你来发掘,详情可移步:

在线代码编辑器

在线代码编辑器这个项目的亮点可就多咯,加上目前正在开发中,我就来简单地聊一下一些亮点吧:

  1. 协同编辑。
  2. 多人视频会议。
  3. 编辑区域的内容无服务器同步到另外一台设备商。

这边的技术栈也全都是采用最新颖的技术

  1. nextjs
  2. zustand
  3. monaco-editor
  4. tailwindcss
  5. webContainer

最后更多详细的亮点正等待着你的发掘:

总结

无聊你是在编写自己的项目亦或是编写公司的项目,只要你去多寻找,多思考,也总能找到一些比较亮的点,切记开发项目永远别停留在开发,要多思考。

如果你也对这些项目感兴趣,不妨来个 star?如果你也想学习或者参与,可以查看我的 ****** 主页查看详情

全部评论

相关推荐

6 11 评论
分享
牛客网
牛客企业服务