前端项目总结

介绍

最近几年,新能源汽车风头正劲,众车企也不断推出电商服务,而众车企创立的App之中,除了卖车业务,也有社区相关服务。某著名车企也需要有一个这样的app,因此早在去年就有实行目前很流行的【super app】计划,就是为了整合原来小程序上的各部分业务,以及社区等新业务到现有APP上来,并在23年9月正式开始。而本人有幸在那时候入场该著名车企,作为社区部分前端tech lead,创立了web h5这个项目,因此也算是前端项目参与者当中经验最丰富的一个。此文主要介绍项目当中的一些内容,以及开发当中遇到的问题,避免以后重复踩坑,使今后的项目更加顺利。

项目采用敏捷开发的方式,每三个月为一个PI(Program Increment),每个PI由6~7个Sprint组成, 每次PI开始之前,会有一个Iterator Planning Meeting,由po(Product Owner)建立卡片,介绍本个PI的卡片内容,开发内容都写在卡片上面,随后对卡片进行估点。开发前需要和po再次沟通,确定卡片上的内容,方可进行开发。

前端部分共分为四个子项目:
1.webh5项目:是集成在APP里面,由native端通过webview在APP中展示,以及jsbridge进行通信,为本项目的核心内容展示,由用户直接访问,以下称为c端。
2. omp后台管理系统:是在web端打开的后台管理系统,运营人员通过登录后台管理系统,对c端的一些内容进行管理控制,以下称为b端
3. 小程序:对于h5和native的内容,部分移植到了小程序上面,目前由于B25资质问题,未能上线
4. 经销商portal:独立抽离出来的一部分,展示经销商内容,也是属于b端,但是由于种种原因,未能上线启用

运行环境分为4个环境: dev, staging, pp, prod

上线过程为: 日常dev、staging环境开发,然后在某个时间点合入pp环境code freeze封板,在pp环境进入回归测试,以及UAT测试,pp环境发现的问题需要登记走cherry pick进行修复,条件都达成以后发prod环境,正式上线

开发历程:

Sprint 8.1(2023.9.14-2023.9.27)
主要是项目准备工作,KT,业务方案设计,技术方案设计
c端: 前端脚手架搭建,准备dev/staging环境,
b端:项目(已有)中创建文件夹

Sprint 8.2(2023.9.28-2023.10.11)
实现jsbridge基础性能力, 登录token接入,
c端:动态发布-入口

Sprint 8.3(2023.10.12-2023.10.25
c端: 动态发布-个人相册,动态发布-官方图库,动态发布-编辑地理位置 动态发布-编辑话题 动态发布-照片/位置权限提示
b端: 动态创建(纯文字、图片、设置发布时间) ,动态管理表格,以及相关功能(动态置顶/沉底,动态编辑,动态加精,动态删除,动态查询) 评论管理(删除,沉底)

Sprint 8.4(2023.10.26-2023.11.8)
c端: 动态评论(删除自己评论,回复评论,回复动态), 动态展示(卡片式动态-pgc、ugc,动态详情),动态点赞
b端:动态创建-pgc内容发布,动态详情查看,动态审核,动态管理(动态排序、动态导出), 话题管理(界面展示、热门标签、话题上下架、话题编辑、排序), 话题详情(界面、关联动态管理、动态参与量查看)

Sprint 8.5(2023.11.9-2023.11.22)
c端: 推荐/社区tab与其他团队联调, 动态feed流(ugc、话题), 动态举报,动态删除, 话题(话题列表、话题主页、话题详情)
b端:动态管理改动逻辑,各模块添加用户手机号,捷伴高尔夫活动管理(活动管理列表、视频管理、编辑、单场排行榜、一杆进洞、图片管理),捷伴高尔夫最新资讯(内容管理、导出),评论管理(评论管理列表、评论详情),评论审核

Sprint 8.6(2023.11.23-2023.12.6)
c端: 技术优化,地区俱乐部列表,地区俱乐部详情(主页、发表动态、主席发布修改俱乐部公告、主席查看会员信息)
b端: 动态创建&动态编辑新增人设号,举报管理,地区俱乐部管理(俱乐部公告查看审核、创建&编辑、排序、删除、界面展示、主席团人员配置),地区俱乐部开放城市(创建,编辑,删除) 税率配置 c端小程序: 动态发布,动态评论,动态展示

Sprint 9.1(2023.12.7-2023.12.20)
c端: pcc会员注册加入,pcc会员到期续费,用户个人基本信息,个人中心动态列表
b端: 动态审核(机审),会员管理甄选好礼(欢迎礼管理,续会籍礼管理) ,会员资格管理,会员积分排行榜(列表,导出),会员信息查询(会员年度周期编辑,筛选列表), 会员维护表(列表,导出)。评论审核
c端小程序: 动态删除,动态举报,动态feed流,话题列表,话题详情,俱乐部列表,俱乐部详情,俱乐部详情发布动态,俱乐部成员列表,俱乐部公告

Sprint 9.2(2023.12.21-2024.01.03)
c端:地区俱乐部(申请加入、审批、退出), 我的礼品,俱乐部会员卡片,俱乐部会员订单(列表,详情),俱乐部会员发票(列表,详情)
b端:地区俱乐部管理(主席团权限配置,成员管理),订单管理(列表,详情)
c端小程序: pcc会员注册加入,pcc会员到期续费,个人动态列表
以及经销商portal相关

Sprint 9.3(2024.01.04-2024.01.17)
c端: 动态分享(朋友圈,复制链接,微信好友),地址管理对接,支付功能对接
b端: 会员信息查询(查看参与活动,添加至黑名单),订单管理-退款,pcc官网管理-会员权益,pcc官网管理-菜单管理,pcc官网管理-横幅管理,pcc官网管理-主页概要管理
c端小程序: 俱乐部会员订单(列表,详情),俱乐部会员发票(列表,详情),地区俱乐部(申请加入、审批、退出) 以及经销商portal相关

Sprint 9.4(2024.01.18-2024.01.31)
c端:加入用户发动态,点赞,评论权限,pcc会员卡片-我的会员,pcc注册入会核销卡券
b端: 月度续会籍率报表(查看,导出),优惠券管理-电子优惠券列表(列表,新增,券码管理,批量制券)
c端小程序: 捷伴高尔夫主页,我的礼品,pcc会员卡片-入口,地址对接,捷伴高尔夫-首页,捷伴高尔夫-定格高光,捷伴高尔夫-我的赛事,捷伴高尔夫-排行榜,pcc会员卡片-我的会员,加入用户发动态,点赞,评论权限,支付功能对接,pcc注册入会核销卡券
以及经销商portal相关

Sprint 9.5(2024.02.01-2024.02.14)
c端小程序: 捷伴高尔夫-赛事一览,我的会员页,发现页对接,捷伴高尔夫-资讯详情,动态分享(微信好友,朋友圈,复制链接),挚想者标签,客服功能

Sprint 9.6(2024.02.15-2024.02.28)
c端小程序: 小程序消息通知

Sprint 9.7(2024.02.29-2024.03.13)
c端小程序: 会员基本信息加入城市、爱好

以上内容V4.5上线(不含小程序、经销商portal)

Sprint 10.1(2024.3.14-2024.3.27)
c端:文章-发布入口, 文章-编辑封面,文章-编辑文本,文章-用户查看卡片式动态, 文章-文章详情,文章-图片预览
b端:文章管理(查询、置顶、加精、沉底、排序、删除、查看详情)

Sprint 10.2(2024.3.28-2024.4.10)
c端:动态视频-拍摄视频,动态视频-相册选择视频,动态视频-卡片、详情页展示,视频压缩上传,文章-点赞,文章-评论举报,文章-查看加精文章,文章-收藏 b端:动态创建-添加视频功能,评论管理、举报管理加入文章相关,文章模板-创建,文章模板-列表查看,文章模板-删除编辑

Sprint 10.3(2024.4.11-2024.4.24)
c端: 动态视频分享,文章分享,文章模板列表,文章模板编辑, 我的收藏(动态、文章),我的发布(动态、文章),我的粉丝,我的关注,关注/取消关注 b端:文章审核(人工审核、机审)
以及经销商portal相关

4月25号V4.5正式上线,APP首次展示社区内容

Sprint 10.4(2024.4.25-2024.5.8)
c端: 用户编辑文章-图文视频混排,草稿列表,编辑草稿详情,保存草稿,删除草稿,屏蔽他人

以上内容V4.6上线(不含小程序、经销商portal)

Sprint 10.5(2024.5.9-2024.5.22)
c端: 高尔夫球队-列表,高尔夫球队-详情页(动态文章、球队、成员信息),高尔夫球队-分享
b端:高尔夫球队管理(列表、新建、编辑、删除、成员管理)

Sprint 10.6(2024.5.23-2024.6.5)
c端: 车聚活动

以上内容V4.7上线(不含小程序、经销商portal)

Sprint 10.7(2024.6.6-2024.6.19)
主要是修复缺陷,优化流程,调整样式

6月17号V4.6正式上线

过程中出现的问题

  1. 4.5上线时,b端动态管理表的文章部分提前露出,原因是该部分未加入feature toggle控制,而且b端没有code freeze等流程,因此直接发了生产环境,造成了一些影响。
    如何避免: 应该在上线前几天的关键时间节点,建卡做好上线准备,列出清单写清楚那些内容是本次版本上线的,每天反复检查,减小出问题的概率。
  2. 分享功能依赖cdn依赖项目的proxy html文件,但该cdn项目未能同步上线,发版时间不同,因此微信内页面的动态分享详情页受到了影响,url解析错误导致白屏。
    如何避免:应该在设计的时候避免存在不同项目的依赖,都放在同一个项目里。或者在proxy里做详细判断,兼容各种不同情况

技术部分(c端):

由于c端为核心部分,因此详细介绍c端,b端的实现已有成熟的模板,这里不进行描述

技术栈
基本架构: typescript + vite + react + jotai + unocss
组件库: @pui
质量保证: husky + eslint + lint-staged + tsc
单元测试: @testing-library/react + jest

其他重点依赖
“typecheck”: “npx tsc --noEmit --skipLibCheck” 进行typescript编译操作,检查错误

postcss-px-to-viewport 前端页面适配,用于将 CSS 中的 px 单位转换为 vw 或 vh 单位

husky ,作用是在代码提交之前触发 git hook,当前命令:
npx lint-staged 对git add的代码进行eslint检测
npx tsc --noEmit --skipLibCheck 触发typescript编译操作,检查错误

typescript-coverage-report 用于测试TypeScript代码覆盖率

@types/react:包含了React类型定义文件的集合

性能指标监控:
vconsole: 需要加入vconsole-stats-plugin.js,在index.tsx中 new VConsoleStatsPlugin(new window.VConsole());

一些重点JS SDK(JS Bridge)

H5 Call Native
本质上, IOS native端会给window里面注入WKWebViewJavascriptBridge,安卓注入WebViewJavascriptBridge。首次运行我们将这个对象注入到window.bridge.jsToNativeBridge中,通过执行其中的callHander,进行相关action调用

const callNative = <T, R>(name: string, params: T, callback?: (response: R) => void) => {
  setupJsToNativeBridge(() => {
    if (window.webkit.jsToNativeBridge) {
      window.webkit.jsToNativeBridge.callHandler(
        'callNativeMethod',
        {
          request: {
            name,
            params,
            version: 1,
          },
        },
        callback,
      )
    }
  })
}

const setupJsToNativeBridge = initCallBack => {
  if (window.webkit?.jsToNativeBridge) {
    return initCallBack && initCallBack()
  }
  const setup = detectMobileOperatingSystem() === 'iOS' ? setupWKWebViewJavascriptBridge : setupWebViewJavascriptBridge
  setup(bridge => {
    window.webkit = {
      messageHandlers: window.webkit.messageHandlers,
      jsToNativeBridge: bridge,
    }
    initCallBack && initCallBack()
  })
}

IOS:

const setupWKWebViewJavascriptBridge = callback => {
  if (window.WKWebViewJavascriptBridge) {
    return callback(window.WKWebViewJavascriptBridge)
  }
  if (window.WKWVJBCallbacks) {
    return window.WKWVJBCallbacks.push(callback)
  }
  window.WKWVJBCallbacks = [callback]
  if (!window.webkit) {
    window.webkit = {
      messageHandlers: {
        iOS_Native_InjectJavascript: window.iOS_Native_InjectJavascript,
        iOS_Native_FlushMessageQueue: window.iOS_Native_FlushMessageQueue,
      },
      jsToNativeBridge: null,
    }
  }
  (window.webkit.messageHandlers.iOS_Native_InjectJavascript as { postMessage: (obj) => void })?.postMessage(null)
}

Android:

const setupWebViewJavascriptBridge = (callback: (obj) => void) => {
  if (window.WebViewJavascriptBridge) {
    return callback(window.WebViewJavascriptBridge)
  }
  if (window.WebViewJBCallbacks) {
    return window.WebViewJBCallbacks.push(callback)
  }
  window.WebViewJBCallbacks = [callback]
  if (!window.webkit) {
    window.webkit = {
      messageHandlers: {
        Native_InjectJavascript: window.Native_InjectJavascript,
        Native_FlushMessageQueue: window.Native_FlushMessageQueue,
      },
      jsToNativeBridge: null,
    }
  }
  (window.webkit.messageHandlers.Native_InjectJavascript as { postMessage: (obj) => void })?.postMessage(null)
}

const callNative = <T, R>(name: string, params: T, callback?: (response: R) => void) => {
  setupJsToNativeBridge(() => {
    if (window.webkit.jsToNativeBridge) {
      window.webkit.jsToNativeBridge.callHandler(
        'callNativeMethod',
        {
          request: {
            name,
            params,
            version: 1,
          },
        },
        callback,
      )
    }
  })
}
  1. navigateToNative
    说明:此接口通过Native拉起新的Native页面,h5页面或者拉起小程序,不处理返回结果,适合对于没有结果处理的页面或者功能拉起
    参数:
    uri:对应页面url,
    Extra:额外参数,为预留参数,可额外扩展,比如样式,标题等
    返回: null
    调用:
	callNative(WebCallNativeActionId.navigateToNative, { uri: params.uri, extra: params.extra || null })
  1. navigateToNativeForResult
    作用: 此接口用于拉起新页面或者服务后,并希望获取处理结果的操作
    参数: 同上
    调用:
	callNative(WebCallNativeActionId.navigateToNativeForResult, { uri: params.uri, requestId: params.requestId, extra: params.extra || null })
  1. close
    作用: 关闭当前web页面
    参数: transResult: 用于返回给前一个页面的值
    调用:
	callNative(WebCallNativeActionId.closeWebPage, { transResult })
  1. showToast
    作用: 调用Native端的Toast组件
    参数:
    · message: 显示想要展示的信息
    · type: ‘success’ | ‘failure’ 显示对应图标
    调用:
	callNative<object, object>(WebCallNativeActionId.showToast, params)
  1. showShareBox
    作用: 调用Native端的分享组件,分享相关按钮功能在native端实现
    参数:
    · item : List类型,每个子项包含以下参数:
    · type: 分享按钮类型: 1.session 微信好友 2.timeline 微信朋友圈 3.link 拷贝链接 4.report 举报 5.delete 删除
    · data: 分享到微信/拷贝链接的信息
    · shareType:微信分享类型:1.webpage 页面 2.miniprogram微信小程序
    · title:分享标题
    · description:分享描述
    · thumbImage:分享缩略图
    调用:
	callNative<object, object>(WebCallNativeActionId.showToast, params)
  1. showPhotoViewerBridge
    作用: 调用Native端的图片预览功能, 图片预览功能在native端实现
    参数:
    · photos:List类型: 图片地址列表
    · initialIndex:Int类型:选择从哪张图片开始显示
    调用:
	callNative<object, object>(WebCallNativeActionId.showPhotoViewer, { photos, initialIndex }, () => Object)
  1. pickPhoto
    作用: 调用Native端的选择图片功能, 选择图片功能在native端实现,拉起系统相册,最多可以选择9张图片,返回选中图片的一些状态信息到h5
    参数:
    · images: List 类型,表示当前已经选中的图片,子项中包含以下参数:
    · status: 上传状态
    · source:来源,可以是官方图库或者本地相册
    · imageUrl:图片地址
    · thumbnailUrl:缩略图地址
    · maxImagesCount: int类型,选择图片张数,可为1-9
    · type: string类型,可以是官方图库(official)或者本地相册(personal)

    调用:

	callNative<
	   NativePickPhoto,
	   NativeResponse<NativePickPhoto>
	 >(WebCallNativeActionId.pickPhoto, nativePickPhoto, res => {
	   callBack(res.data)
	 })
  1. takeCameraPhoto
    作用:调用native端的拍照功能,返回选中图片的一些状态信息到h5
    参数:无
    调用:
	callNative<
	   object,
	   NativeResponse<NativeTakeCamera>
	 >(WebCallNativeActionId.takeCameraPhoto, params || {}, res => {
	   callBack(res.data)
	 })
  1. getLocation
    作用:从Native端获取定位数据,包括latitude和longitude
    参数:无
    调用:
	callNative<object, NativeResponse<Location>>(WebCallNativeActionId.getLocation, {}, res => {
	   callBack(res.data)
	 })
  1. openPaymentCNForResult
    作用:拉起native端的支付收银台功能,支付收银台的所有功能都在native端实现
    参数:
    · clientId: 业务订单系统标识,格式为string,例如 cm-community-service
    · orderNo: 订单编号,格式为string
    · amount: 应付金额,格式为string,
    调用:
	callNative<unknown, NativeResponse<unknown>>(WebCallNativeActionId.openPaymentCNForResult, { serviceName, orderNo, amount })
  1. takeCameraVideo
    作用:调用native端的拍摄视频功能,返回拍摄视频的一些状态信息到h5
    参数:无
    调用:
	callNative<
		   object,
		   NativeResponse<NativeTakeCamera>
		 >(WebCallNativeActionId.takeCameraVideo, {}, res => {
		   callBack(res.data)
	 })
  1. showActionSheetBridge
    作用:调用native端的底部单选框组件
    参数:
    · title:string类型,标题
    · item: ShowSheetSectionItem[]类型,子项代表每一个按钮的信息,包含以下数据:
    · id: string类型,唯一标识
    · title: string类型,标题
    · type: 按钮类型,可以为’default’ | ‘warning’
    调用:
	callNative<ShowSheetParams, NativeResponse<DataType>>(WebCallNativeActionId.showActionSheet, params, res => {
	   callBack(res.data)
	 })

名称:requestPermission
作用:拉起Native端的权限请求窗口
参数:
· type : string类型,请求权限的类型,photo:相册权限,camera:拍照权限,location:定位权限
调用:

callNative<PermissionParams, NativeResponse<PermissionResponse>>(WebCallNativeActionId.requestPermission, { type }, res => {
   callBack(res.data)
 })

Native Call H5

  1. onDarkModeChanged
    获取深浅模式: Native设置深浅模式以后,需要通知给h5,变换对应组件样式,返回结果为字符串:light/dark

  2. onPaymentResult
    获取支付结果: Native收银台支付完成以后,需要通知给h5,获取支付结果信息,返回结果为TPaymentResult对象:

{
	orderNo: string
 	paymentStatus: {
	   type: 'succeed' | 'failed'
	   code?: EPaymentResultCode
	   message?: keyof typeof EPaymentResultCode
	   data?: unknown
 	}
}
  1. onPublishAssetUpdated
    获取图片更新状态:native端图片上传完成以后,需要通知给h5端该图片的状态信息,h5端拿到信息以后更新图片。返回结果为图片对象:
source: string, 图片来源,可以是官方图库或者本地相册
       status: string, 图片上传状态,可以是 'success' | 'failure' | 'loading'
       imageUrl: string, 图片原图url
       thumbnailUrl: string, 图片缩略图url
       localIdentifier: string, 图片唯一标识,用于判断返回的信息是属于哪张图片
       externalData: {urlOriginFileId: string, urlThumbnailFileId}, 用于存放一些图片额外信息
  }

code review

定于每周五下午进行一次code review,形式为各团队成员展示本周提交的代码,大家一起进行点评,为了持续提高和保证代码质量,保持团队代码风格一致,提早发现问题,降低代码风险

  • 简单交代背景:做的什么卡什么业务,可以打开jira 或设计稿 讲解代码实现方案,逐个commit 讲解自己的代码
  • 如果有技术问题,可以在code review期间提出来大家一起讨论
  • 其他人在听的同时,针对好的代码积极给出肯定和表扬,发现问题也积极主动提出,如需优化并达成一致,将问题记录下来,之后做代码优化,在下次review时,展示修复的结果。

过程中的一些重点问题:

  1. 一个方法中如果有大量的代码需要进行拆解重构成小方法
  2. 对于传参数量非常多的某些组件,可以优化参数数量,或者重构小组件
  3. 可以逐步移除一些组件memo的使用
  4. 需要注重传参类型,规范函数参数格式,避免报错
  5. 每个组件应当放入单独文件夹,文件夹内容应当包括tsx组件、css样式文件、测试文件,以及子组件文件夹,增强内聚性
  6. 在写代码的时候,就应当注重规范,设计成内聚性强的小组件,一个组件做单一功能,避免之后重构
  7. 对于分享页面的处理,应避免直接访问原地址,需要通过proxy html页面进行访问,一是为了安全性避免暴露重要信息,二是统一维护方便调整

团队纪律

  • CI构建失败时,停止Push 代码,优先修复
  • Push 代码之前在本地通过所有测试和规范检查
  • CI失败不过夜,修复后再下班或者回滚第二天修复后再提交
  • 每天下班本地不能留存代码
  • 27
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: nginx 是一个常用的高性能的开源 Web 服务器软件,它可以用来发布前端项目。在发布前端项目时,可以通过在 nginx 的配置文件中设置别名来实现。 别名是 nginx 配置文件中的一个指令,它可以用来指定前端项目文件的路径,使得在访问前端项目时可以使用自定义的 URL 路径。 在 nginx 的配置文件中添加别名的语法如下: ```bash location /别名 { alias /前端项目路径; } ``` 其中,`别名` 是自定义的 URL 路径,可以根据需要设置。`前端项目路径` 是前端项目的文件路径,需要根据实际情况进行设置。 举个例子,假设我们有一个前端项目的文件路径是 `/var/www/html/myproject`,我们想要通过 URL 路径 `/project` 来访问该项目,那么可以在 nginx 的配置文件中添加如下的配置: ```bash location /project { alias /var/www/html/myproject; } ``` 配置完成后,当访问 `http://localhost/project` 时,nginx 将会返回前端项目的内容。 通过设置别名,我们可以灵活地指定前端项目的路径,使得在访问前端项目时可以使用更简洁、易于理解的 URL 路径。这样不仅可以提高项目的可读性,也能够更好地维护和管理前端项目。 ### 回答2: 在使用nginx发布前端项目时,我们可以通过配置别名来指定项目的访问路径。 首先,我们需要编辑nginx的配置文件,可以在nginx的安装目录下找到名为nginx.conf的文件。在该文件中找到server块,这是nginx配置的主要部分。 在server块中,我们可以使用location指令来配置前端项目的别名。例如,假设我们的前端项目位于/var/www/html目录下,我们可以使用以下配置来定义别名: location /项目名 { alias /var/www/html; } 在上述配置中,/项目名 是我们要给项目指定的别名,alias指令后面是项目的实际路径。 完成配置后,我们需要重启nginx使其生效。可以通过运行以下命令来重启nginx: sudo service nginx restart 此时,我们可以通过访问http://服务器ip地址/项目名 来访问前端项目。通过配置别名,可以方便地指定项目的访问路径,使得前端项目能够直接通过项目名进行访问,而无需指定完整的项目路径。 总结起来,nginx发布前端项目时,可以通过配置别名来指定项目的访问路径。首先编辑nginx的配置文件,然后在server块中使用location指令来配置别名。最后重启nginx使其生效,可以通过访问http://服务器ip地址/项目名 来访问前端项目。 ### 回答3: 在nginx中发布前端项目的别名是通过设置nginx的location指令来实现的。在nginx的配置文件中,可以使用location指令来定义前端项目的路径,并可以指定别名。 例如,将前端项目的路径设置为"/var/www/html",并指定别名为"/static",可以在nginx的配置文件中添加以下代码: ``` location /static { alias /var/www/html; index index.html; } ``` 上述配置中,当访问服务器的/static路径时,nginx将会将请求映射到/var/www/html目录下寻找对应的文件。如果存在index.html文件,则会直接返回该文件,如果不存在则返回404错误。 通过设置别名,可以使前端项目的访问更加友好和简洁。我们可以使用简短的URL来访问前端项目,而不需要暴露实际的项目路径信息。 此外,还可以通过添加更多的location指令来配置其他的前端项目路径和别名,以满足不同前端项目的需求。当然,在修改nginx配置文件后,需要重新加载配置文件使其生效。 总之,通过设置nginx的location指令并指定别名,可以将前端项目发布到指定的路径,并使用别名来访问项目,提高了访问的简洁性和安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值