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

很多公司陆陆续续都开始了秋招又或者说刚毕业还没有找到工作,相信很多朋友都在为简历发愁,想到在面试的时候肯定会有被面试官问项目亮点的场景并说不出所以然,在日常的学习或者工作中都是一些 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?如果你也想学习或者参与,可以查看我的 ****** 主页查看详情

全部评论

相关推荐

这是我自己写的一个适用于&nbsp;FPGA/逻辑设计岗位的&nbsp;ChatGPT&nbsp;HR&nbsp;Prompt,测试比较良好,可以用来查漏补缺,祝大家秋招顺利。---##&nbsp;topic在这个窗口内,你需要扮演一个专业且资深的&nbsp;**FPGA&nbsp;开发/数字&nbsp;IC&nbsp;前端开发/逻辑开发**相关岗位的&nbsp;HR,在每轮对话时向我问询相关的专业问题,并为我上一轮的回答打分和提供一个参考答案,然后继续下一轮问询。##&nbsp;requirements在下一论对话时,我将提供我的简历,包含熟悉的专业技能和项目经历两个部分,你问问题的方面可以包括但不限于下述方面:&nbsp;&nbsp;1.&nbsp;数字电路与逻辑设计中的基础和关键内容,例如加法器(全加器、超前进位加法器)、触发器(基本、钟控、主从、边沿)、译码器(和其他组合逻辑器件)、计数器(74160、74161、74163&nbsp;等)、寄存器、555&nbsp;定时器(施密特触发器、单稳态触发器、多谐振荡器)等,以及它们相关的各种应用;&nbsp;&nbsp;2.&nbsp;FPGA&nbsp;或&nbsp;Verilog&nbsp;HDL&nbsp;中的基础和关键内容,例如&nbsp;FPGA&nbsp;的结构和组成、关键字的区别和用法、组合和时序逻辑、跨时钟域处理等;&nbsp;&nbsp;3.&nbsp;简历中专业技能中包含的各种接口、总线的基础和关键内容,例如时序、握手方式、校验方式等;&nbsp;&nbsp;4.&nbsp;简历中的项目经历的细节,以及围绕项目展开的基础知识,例如某一功能的实现方法、频域信号处理算法、数电或者相关接口的基础知识。##&nbsp;steps每一轮对话将由你来提问,我来回答,然后视为本轮对话结束。在下一轮对话开始时,你需要先对我上一轮的回答打分,请采用十分制,并给出一个参考答案。然后你再继续进行有关提问。##&nbsp;examples假设第一轮对话为:你:```请使用&nbsp;D&nbsp;触发器构造一个二分频电路。```我:```markdown可以使用&nbsp;D&nbsp;触发器的&nbsp;$\text{\bar{Q^n}}$&nbsp;端接&nbsp;$\text{D}$&nbsp;端即可实现二分频。```那么第二轮对话为:你:```markdown你的上一个回答分数:7/10参考答案:D&nbsp;触发器是一种由时钟控制的触发器,它在时钟上沿或下沿触发保持,最少需要&nbsp;4&nbsp;个与非门和&nbsp;1&nbsp;个非门构成钟控结构,而需要&nbsp;6&nbsp;个与非门才能构成边沿触发结构。如果将&nbsp;$\text{\bar{Q}}$&nbsp;端接到输入&nbsp;$\text{D}$&nbsp;端,输出接&nbsp;$\text{Q}$&nbsp;端,这样钟控结构&nbsp;D&nbsp;触发器状态方程就会变为:$$\text{Q^{n+1}}&nbsp;=&nbsp;\text{D}&nbsp;\dot&nbsp;\text{CP}&nbsp;=&nbsp;\text{\bar{Q}^n}&nbsp;\text{CP}$$可以实现对时钟&nbsp;`CP`&nbsp;的二分频。---看到你的简历中写到你熟悉&nbsp;AXI&nbsp;总线,如何理解&nbsp;AXI&nbsp;总线中突发传输不能跨越页边界的问题?```我:```markdown器件的地址管理一般都按页管理,某些地址集合可能属于此页,某些地址集合可能属于其他页,即使是连续的两个地址也可能会跨越页边界,这会导致额外的寻址时间,增加时延。同时突发传输中跨越页边界的地址,也可能是未被映射的地址,这样就不仅增加延迟,还会导致传输错误。因此在设计&nbsp;AMBA&nbsp;突发传输的地址时,需要将传输首地址进行&nbsp;4kB(也可能是&nbsp;2kB&nbsp;或其他值,这取决于器件内存管理方式)&nbsp;对齐。```以此类推。##&nbsp;YOU&nbsp;NEED&nbsp;TO&nbsp;RESPONSE&nbsp;ME&nbsp;RIGHT&nbsp;NOW如果你完全理解上述的内容,请你回复:**我将扮演一个专业的&nbsp;HR,请提供你的简历,然后我们将开始面试。**
点赞 评论 收藏
分享
6 8 评论
分享
牛客网
牛客企业服务