个人创业中的全栈开发经验

16,205 阅读5分钟

前言

个人项目开发创业半年有余,两个项目全部扑街,一无所获。

仔细想来其实也不是什么都没有得到,因为现状就是,我创业开始前能预想到的最坏情况,哪怕一毛钱都挣不到,但是也可以从中积累一些经验,比如全栈开发经验。

我过去7年的工作都是在从事前端开发,从最开始的IPTV 开发,用原生JS、JQuery 开发运行在机顶盒上JSP 页面;到18年,创建了项目组的第一个Vue 项目,那时候我才算是开始“现代”前端的工作;再到21年来上海,在新公司开始全面使用React + TS。也就是说,时至创业开始,我所有的工作经验只有前端开发,后端相关的只有自己瞎折腾的项目,没有真正应用到实际项目中的,这次也算是逼着自己进步了一把。

技术选型

前端 - 后台管理系统:React + TS,用了Antd 的组件库提供的模板直接创建项目

前端 - 微信小程序:原生微信小程序开发 + Vant Weapp

服务端:微信云开发

为什么要选用以上技术栈,只有有一个原因,就是成本极低!非常低!前后端全部用JS 搞定;后台管理系统部署在腾讯云的Web 应用托管上,直接免去运维工作。说个题外话,前几年搞个人网站的,服务器是薅的阿里云的羊毛,结果就是啥活都得自己干,用Express 框架开发的后端服务,用Nginx 做代理,结果并发超过100个 服务器直接挂掉。。

现在这一套技术栈,几乎没有学习成本,腾讯云的Web 应用托管集成了CI 工具,提交代码到线上分支后直接自动部署,用了半年多,网站、小程序都没有挂掉过。(我真不是腾讯的托。。)

后台管理系统

React、TS、Antd 业务开发技术不多赘述,讲讲怎么在Web 端请求微信云开发的接口吧。

微信云开发提供了可访问云服务的Web Sdk,引入sdk 后,只需要进行简单的初始化,即可访问接口。

云开发登录授权配置,打开匿名登录

image.png

示例代码

处理请求

import cloudbase from "@cloudbase/js-sdk";
...
const env = ""; // 环境id
const clientId = ""; 终端id

// 创建实例
const app: any = cloudbase.init({
  env,
  clientId,
});

const auth = app.auth({
  persistence: "local",
});

...
// 请求方法
export const cloudFn = async (
  type: string,
  params?: any
): Promise<any> => {
  // 判断登录态
  if (!auth?.hasLoginState()) {
    localStorage.clear();
    await auth.signInAnonymously();
  }

  const res = await app.callFunction({
    name: "xxxx", // 云函数名称
    data: { type, data: options?. }, // 传参
    parse: isDev, // 环境
  });

  // 根据自己的业务方式处理返回数据
  ...

处理接口

import { cloudFn } from "@/utils";

export const xxx = (params: API.xxx) => {
  return cloudFn("name", params);
};

Web Sdk 官方文档:docs.cloudbase.net/api-referen…

部署

提交代码到部署分之后,会自动部署,访问web 应用托管,会提供一个默认访问的域名,可以直接访问,但是不推荐生产使用,只需要再配置一个域名就好了。

微信小程序

如果没有开发过微信小程序,去看一下官方文档,前端基本可以无成本上手,参照官方文档开发就好;为什么组件库选择Vant Weapp,基本补全了官方没有提供的组件,使用方式也很简单,实际使用后体验不错,值得推荐。

微信云开发

我用Java、python、node 都写过后端接口,对于一个前端来说,单论简单、好上手而言,微信云开发,我愿称之为YYDS!就两个字,简单!

官方提供了请求的方法,我对其简单的封装了一下,如果觉得不错,尽管拿去用,如果有不完善的,还请指正

示例代码

云函数入口 index.js
const user = require('./user);

exports.main = async (event, context) => {
  switch (event.type) {
    case "userGet":
    case "userUpdate":
      return await user.main(event, context);
  

    default:
      return {
        code: -1,
          msg: '接口不存在'
      }
  }
};
user 入口
const get = require("./get");
const update = require("./update");

exports.main = async (event) => {
  const apiType = event.type
  const data = event.data || {};

  if (apiType === 'userGet') {
    return await get.main(data);
  };

  if (apiType === 'userUpdate') {
    return await update.main(data);
  };
};
user/get.js
const {
  dbGet, // 通用get 方法 (见后问)
  filterParams, // 清除异常参数,比如空字符串,null 等
} = require("../../utils");
const check = require('./check');

exports.main = async (data) => {
  // 校验参数
  if (check(data)) return {
    code: -1,
    msg: check(data),
  }

  const params = {
    offset: data.offset,
    limit: data.limit,
    name: data.name,
  };


  // 模糊搜索
  if (params.name) {
    params.name = {
      $regex: ".*" + params.name,
      $options: "i",
    };
  }

  return await dbGet("user", filterParams(params));
};
user/update.js
const {
  dbUpdate
} = require("../../utils");
const check = require('./check');

exports.main = async (data) => {
  if (check(data)) return {
    code: -1,
    msg: check(data),
  }


  const params = {
    _id: data._id,
    name: data.name
  };

  return await dbUpdate("user", params);
};
utils.js
const cloud = require("wx-server-sdk");

// 初始化云环境
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV,
});

const db = cloud.database();

async function dbGet(
  databaseTable, // 表名
  params, // 参数
  orderByKey = "", // 排序参数
  order = "desc" // 排序方式
) {
  const pageInfo = {
    offset: params.offset || 1,
    limit: params.limit || 10,
  };
  delete params.offset;
  delete params.limit;

  try {
    // 获取总数
    const resCount = await db
      .collection(databaseTable)
      .where(params)
      .count();
      
    const resCountData = formatRes(resCount) 
    if(resCount?.code !== 0) {
      return resCountData
    }
    
    // 总数是0,直接返回数据
    if(resCount?.data === 0) {
        return {
          code: 0,
          data: { data: [] },
          total: 0,
        }
    }
    
    // 获取数据
    const res = await db
      .collection(databaseTable)
      .where(params)
      .skip((pageInfo.offset - 1) * pageInfo.limit) // 分页
      .limit(pageInfo.limit) // 最多几条
      .orderBy(orderByKey, order) // 排序
      .get();
    
    // 处理返回数据
    const resData = formatRes(res);
    if(resData?.code === 0) {
        return {
          code: 0,
          data: {
            data: resData?.data || [],
            total: resCountData?.data || 0
          },
        }
    } else {
      return resData
    }
  } catch (error) {
    return {
      code: -2,
      data: null,
      msg: "请求失败",
    };
  }
}

async function dbUpdate(databaseTable, updateData) {
  let res;
  let isSuccess = false;
  try {
    const params = updateData;
    
    if (updateData._id) {
      // 编辑
      delete params._id;
      res = await db.collection(databaseTable).doc(updateData._id).update({
        data: params,
      });
      
      if (res.errMsg === "document.update:ok") {
        isSuccess = true;
      }
    } else {
      // 新增
      res = await db.collection(databaseTable).add({
        data: params,
      });
      
      if (res.errMsg === "collection.add:ok") {
        isSuccess = true;
      }
    }
    if (isSuccess) {
      return {
        code: 0,
        _id: res._id,
        msg: `${updateData._id ? "更新" : "新增"}数据成功`,
      };
    } else {
      return {
        code: -1,
        data: null,
        msg: `${updateData._id ? "更新" : "新增"}数据失败`,
      };
    }
  } catch (error) {
    return {
      code: -1,
      data: null,
      msg: `请求服务器失败,${updateData._id ? "新增" : "更新"}数据失败`,
    };
  }
}

function formatRes(res) {
  const cloudFnMsgList = ["document.get:ok", "collection.get:ok", "collection.count:ok"];

  if (cloudFnMsgList.includes(res?.errMsg)) {
    return {
      code: 0,
      data: res.data || res.total,
    };
  } else {
    return {
      code: -1,
      data: null,
      msg: "请求服务器失败",
    };
  }
}

module.exports = {
  dbGet,
  dbUpdate,
  formatRes,
};

差不多到此就结束了,Web 端的后台管理系统,微信小程序的后端接口实现了,并且可以互通,这种方式是我实践过,在保证业务、性能、稳定的前提下,最低成本的全栈开发方案,如果有其他更好的方案,欢迎讨论。

最后再说点感悟,销售真不是人干的

ps:辛苦点个赞,能点个关注就更好了,您的关注、点赞、收藏是我继续创作的动力