活动报名小程序模板(一)

1,809 阅读16分钟

我的仓库地址:LindsBravill的仓库

1. 项目结构

1.1 git协作开发

这是我第一次参与开源项目,以前对git结构和开源协作的流程其实并不了解,在提PR的过程中还是有很多疑惑所以记录一下。参照TDsign协作开发文档

  1. 协作基本流程:

    • 源仓库提供项目的基本结构和配置等,参与开源开发的协作者将源仓库fork到自己的仓库进行开发;

    • 协作者将自己的仓库代码clone到本地仓库,关联自己的仓库的同时,关联远程的上游仓库,即源仓库;

    • 协作者在本地创建develop​​分支,而非使用默认分支main​​,在develop​​分支上进行开发;

      同时在develop​​分支的基础上,创建新分支:

      feat/xxx​​表示新功能开发;

      fix/xxx​​表示日常问题修复;

    • 协作者开发完成并且项目通过eslint验证后,将代码提交到自己的仓库中;

    • 最后,在源仓库提交Pull Request;

  2. 实践流程:

    按照协作开发文档来:

    • fork源仓库:在gitHub主页即可进行;

    • 创建本地仓库并clone:本地新建项目文件夹;

      $ git init
      $ git clone git@github.com:${USER}/${PROJECT}.git
      
    • 配置Git账户信息:进入项目文件夹内;

      ## cd ${PROJECT} 本地仓库目录
      $ git config user.name "your name"
      $ git config user.email "your email address"
      
    • 关联上游仓库:

      $ git remote add upstream https://github.com/Tencent/${PROJECT}.git
      $ git remote -v
      > origin    git@github.com:${USER}/${PROJECT}.git (fetch)
      > origin    git@github.com:${USER}/${PROJECT}.git (push)
      > upstream    https://github.com/Tencent/${PROJECT}.git (fetch)
      > upstream    https://github.com/Tencent/${PROJECT}.git (push)
      
    • 新建并进入开发的分支:

      $ git branch develop
      $ git checkout develop
      $ git checkout -b feat/xxx
      

      此时,项目中存在的分支: image-20230917203522-2d2cnfp.png 需要注意的一个点是,在真正使用git协作以前,我天真地以为提pr就是将写好的文件上传上去,合并pr就是将提pr那一刻更新的代码合并到源仓库中。也是这种想法,让我犯了不少很傻的错...实际上,提交pr就是将该分支连接到源仓库的主分支上,合并的是实时更新的分支,而不是提交pr那一刻的文件。因此在开源项目开发中要格外小心管理好自己的各个分支,在合并自己的分支的时候也要注意不要合并错方向。

1.2 微信小程序引擎

  1. 微信小程序引擎作用:

    微信小程序引擎是微信客户端内部的一个核心组件,它提供了小程序运行所需的一切基础设施和功能,包括代码解析、页面渲染、事件处理、数据管理、网络通信等与小程序相关的各种操作和功能:

    • 解析和运行代码: 引擎负责解析小程序的代码,包括JavaScript代码、wxml文件(用于定义页面结构)、wxss文件(用于定义页面样式)等。它将代码转换为可执行的指令,然后执行这些指令以构建小程序的页面和功能。
    • 管理页面生命周期: 小程序引擎管理小程序页面的生命周期,包括页面的创建、加载、显示、隐藏、卸载等各个阶段。它会触发相应的生命周期事件,让开发者可以在不同阶段执行自定义的逻辑。
    • 数据绑定和更新: 引擎支持数据绑定,允许开发者将数据与页面元素关联起来。当数据发生变化时,引擎会自动更新页面上相应的元素,实现动态页面效果。
    • 事件处理: 引擎监听和处理用户的交互事件,如点击、滑动、输入等。它负责分发这些事件给页面中相应的事件处理函数,让开发者可以响应用户的操作。
    • 资源管理: 引擎负责管理小程序的资源,包括图片、音频、视频等。它会处理资源的加载和释放,以确保小程序的性能和资源使用效率。
    • 网络请求: 引擎提供了一组API,允许小程序进行网络请求,与服务器通信,获取数据或上传数据。
    • 安全性: 引擎实施了一系列安全策略,以确保小程序的安全性。它会限制小程序的访问权限,防止恶意操作或对用户数据的滥用。
    • 性能优化: 引擎会执行一些性能优化策略,如预加载页面、资源的缓存和懒加载,以提高小程序的加载速度和响应性能。
    • 与硬件交互: 小程序引擎支持与设备硬件的交互,如摄像头、定位、蓝牙等,以实现更丰富的功能。
  2. 初始化小程序流程:

    当用户第一次打开一个微信小程序时,微信小程序引擎会执行一系列操作来初始化和加载小程序:

    • 下载小程序代码包: 当用户点击小程序图标或扫描小程序码时,微信客户端会向微信服务器发送请求,请求下载小程序的代码包。这个代码包包含了小程序的所有必要文件,如JavaScript代码、wxml文件、wxss文件、以及资源文件。
    • 解压缩和缓存代码包: 下载完成后,微信客户端会将代码包解压缩并缓存在本地。这样可以提高小程序的加载速度,因为后续打开小程序时可以直接使用本地缓存的代码。
    • 执行小程序初始化: 微信小程序引擎会执行小程序的初始化代码,包括执行小程序的 App​​ 实例化和生命周期函数。这些函数可以用来配置小程序的全局行为,如设置全局数据、注册全局事件处理程序等。
    • 展示小程序界面: 一旦小程序初始化完成,引擎会展示小程序的初始页面,通常是小程序的首页。此时,wxml 文件会被渲染为页面的结构,wxss 文件会被应用于页面的样式。
    • 加载页面资源: 如果小程序的页面包含了网络请求或者加载其他资源(如图片、音频、视频等),引擎会开始加载这些资源。这些资源可能来自小程序的服务器或者其他网络资源。
    • 事件处理: 引擎会开始监听用户的输入事件,如点击、滑动等,以响应用户的操作。这包括页面中的事件监听器以及小程序的全局事件处理。
    • 后台任务: 引擎可能会在后台执行一些任务,例如预加载其他页面、更新数据、缓存数据等,以提高小程序的性能和响应速度。

1.3 项目打包

  1. 打包流程:

    微信小程序开发者工具在打包并上传小程序代码时,会执行一系列操作处理小程序:

    • 代码压缩和优化: 微信小程序平台会对小程序代码进行压缩和优化,以减小代码体积,提高运行性能。这包括删除不必要的空格、注释,以及合并、混淆 JavaScript 代码等操作。
    • 生成小程序包体结构: 平台会根据小程序目录结构,生成小程序包体结构,包括生成页面、组件、模板等的编译后文件。这些编译后的文件将用于小程序的运行。
    • 生成配置文件: 微信小程序平台会生成小程序的配置文件,例如 app.json​​、project.config.json​​ 等,这些文件包含了小程序的配置信息、页面路径、导航栏样式等。
    • 资源文件处理: 平台会处理小程序中的资源文件,如图片、音频、视频等。这包括将图片进行压缩、生成不同尺寸的图片以适应不同设备,然后将资源文件放置在合适的位置。
    • 生成小程序二维码: 在上传过程中,平台会生成小程序的二维码,供用户扫描以访问小程序。这个二维码通常在开发者工具中提供,用于在测试和预览阶段方便扫码体验。
    • 发布版本号管理: 平台会管理小程序的版本号,并确保版本号的唯一性。每次上传新版本时,版本号需要递增。
    • 代码上传和审核: 一旦所有文件和配置都准备好,平台会将小程序代码上传到微信开发者平台,其中包括小程序的描述、版本号等信息。上传后,代码需要经过审核,以确保符合微信的规范和政策。
    • 发布小程序: 审核通过后,可以选择发布小程序,使其对所有用户可见。此时,小程序将在微信中可供用户搜索、扫码访问,并在微信的小程序商店中展示。

2. 视图样式

2.1 修改组件库默认样式

  1. 问题描述:

    使用TDesign中的轮播图组件t-swiper + t-swiper-nav​​,实现下面效果: image-20230815153453-qpnq9nf.png

    需求中,图片的宽高为{ height: 159.19px, width: 238px }​​,图片间距为12px​​;

    而在组件中,默认样式呈现效果是高度192px​​宽度307px​​,图片间距为24px​​。

  2. 问题分析:

    修改组件默认样式的问题是没有唯一对应的解决方法的,通常需要通过不断尝试找到解决方案。在寻找实现方法前,最重要的是**分析组件中形成当前默认样式的原因,**再参考组件提供的API进行修改或样式覆盖等。

    • 分析组件:

      打开微信小程序开发者工具的控制面板,可以看到使用的组件解析成js代码后的样子:

      使用的组件代码:

      <view class="swiper">
        <view class="title">热门推荐</view>
        <view class="card-theme">
          <t-swiper 
           current="{{current}}" autoplay="{{autoplay}}" duration="{{duration}}" interval="{{interval}}" 
           bindchange="onChange" navigation="{{ { type: 'dots' } }}" list="{{swiperList}}" 
           image-props="{{ { shape: 'round' } }}" previousMargin="34px" nextMargin="34px" t-class-nav="card-theme-nav" />
        </view>
      </view>
      

      解析后的代码: image-20230815154830-dmg8sal.png

      可以看到组件代码被解析成了轮播图和换页点点两个部分,两者由一个标签<view class="class t-class t-swiper">​​包裹;在轮播图<swiper>​​部分,有多个<swiper-item>​​标签,这些就是轮播的图片。

      现在针对每个<swiper-item>​​的属性进行分析,分析每个和宽高有关的属性image-20230815155515-j8d0gt2.png

      宽高都为100%​​,说明是由其外部容器决定的,图片高度即为<swiper>​​的高度;宽度可能取决于margin/padding​​等其他,这里的padding​​为一个变量,查看组件库的官方定义:

      .card-theme {
        --td-swiper-radius: 0;
        --td-swiper-item-padding: 0 12rpx;  // 默认值是左右padding为12px
        --td-swiper-nav-dot-color: #e7e7e7;
        --td-swiper-nav-dot-active-color: #0052d9;
      
        padding-bottom: 18px;
      }
      
      .card-theme .card-theme-nav {
        bottom: -18px;
      }
      
      .scale-candidate {
        height: 126px !important;
      }
      

      那么查看外部容器<swiper>​​的属性: image-20230815155908-aye596m.png

      有默认高度为192px​​;同时有previous-margin+next-margin​​两个属性来表示前后两张图片实现的大小,查看组件官方API说明: image-20230815160321-wf8ajcn.png

      也就是说:

      • 图片的高度就是<swpier>​​的高度;

      • 图片的宽度是:视口的宽度 - previous-margin​​ - next-margin​​ - padding * 2​​;

        注意这里的previous-margin/next-margin​​是包括一个padding​​的,可以通过控制面板的盒子模型分析出来。

      分析到这里,就知道如何修改了。

    • 修改样式:

      通常需要根据组件库官方给的API说明来修改,否则可能样式无效。

      • 高度:

        查看官方文档可知<swpier>​​的高度是由<t-swiper>​​的属性height​​决定的,因此只需要将修改这个属性:

        <t-swiper 
             height="159.19px"
             current="{{current}}" autoplay="{{autoplay}}" duration="{{duration}}" interval="{{interval}}" 
             bindchange="onChange" navigation="{{ { type: 'dots' } }}" list="{{swiperList}}" 
             image-props="{{ { shape: 'round' } }}" previousMargin="34px" nextMargin="34px" t-class-nav="card-theme-nav" />
        
      • 宽度:

        现在需要将图片的宽度设置为283px​​,已知视口宽度为375px​​;图片间距由内外边距两个决定,其中margin​​为0,需要的间距为12px​​,那么padding​​就为12px/2 = 6px​​;因此previous-margin = next-margin = (375 - 283 - 6*2)/2 = 40px​​。

      因此最终的代码为:

      <!--index.html-->
      <view class="swiper">
        <view class="title">热门推荐</view>
        <view class="card-theme">
          <t-swiper height="159.19px" current="{{current}}" autoplay="{{autoplay}}" duration="{{duration}}" interval="{{interval}}" bindchange="onChange" navigation="{{ { type: 'dots' } }}" list="{{swiperList}}" image-props="{{ { shape: 'round' } }}" previousMargin="40px" nextMargin="40px" t-class-nav="card-theme-nav" />
        </view>
      </view>
      
      /* index.less */
      .card-theme {
        --td-swiper-radius: 0;
        --td-swiper-item-padding: 0 6px;
        --td-swiper-nav-dot-color: #e7e7e7;
        --td-swiper-nav-dot-active-color: #0052d9;
        padding-bottom: 18px;
        margin-top: 16px;
      }
      
      .card-theme .card-theme-nav {
        bottom: -18px;
      }
      
  3. 总结思考:

    在刚开始解决这个问题的时候会胡乱的设置样式来尝试,但是毫不意外的无效,因为都被原有的样式覆盖了。组件的原有样式都是在组件的style​​属性中设置的,属于内联样式,有较高优先级,因此自己给组件设置样式通常是无效方法。所以最重要的是要分析组件默认样式形成的原因,顺着组件形成的逻辑来进行修改。但是分析组件样式形成原因也没有像上面那样简单的,还是需要多次尝试。

2.2 文本图标对齐

经常对遇到关于文本、图标以及两者之间对齐的问题,要找到解决方法,应该先理解css相关属性的原理而不是死记硬背方法:

  1. 水平方向:

    下面给出几种方案和基本原理:

    • text-align​​:

      这个属性是控制元素内的**文本(包括图标)**在水平位置上如何对齐,也就是说它不控制元素本身如何对齐,而是元素内的文本如何对齐;

      该属性的默认值一般是left​​,使用center​​能使文本居中,即文本的首尾分别离元素左右边框的距离相等:

      .box{
        text-align: center;
      }
      
    • margin​​:

      这个属性是利用元素盒子模型的外边距自动分配机制实现居中效果的,也就是说对于固定宽度的**块级元素,**设置左右外边距为auto​​时浏览器会将左右、上下外边距平均分配,因此,不同于前面两个属性,这个属性是会对整体布局有影响的:

      .text{
        margin: 0 auto;
      }
      
    • 弹性盒子:

      justify-content​​这个属性原本是用于调整弹性盒子内元素如何排列的,所以需要同时将该元素设置为弹性盒子:

      .box{
        display: flex;
        justify-content: center;
      }
      
    • 绝对定位

      这个方法则是通过文本相对在盒子内的位置,进行水平调整实现的居中:

      .text{
          position: absolute;
          left: 50%;
          transform: translateX(-50%);
      }
      
    • 网格布局:

      类似于弹性盒子布局,利用网格布局中的特有属性place-items​​对元素即文字进行居中对齐:

      .container {
          display: grid;
          place-items: center;
      }
      
  2. 垂直方向:

    对于垂直方向有几个概念需要搞清楚:

    • 文本中线:

      css中没有明确定义这个概念,但是为了方便区分文本基线的定义并解释后面的概念,所以提出这个概念。文本中线就是将文字的大小均分,距离上边和下边都相等的一条线。比如,文本大小是18px​​,那么文本中线就在距离文本顶部边缘和文本底部边缘都是9px​​的位置。

    • baseline​​:文本基线

      一般来说,对于英文基线位置就是字母x的底边缘,而中文对基线的位置没有并且的定义。但是不论是中文还是英文,文本基线都会比文本字面下缘高一点: image-20230817153636-jbc56j4.png

    • line-height​​:

      是指给文本框出一个高度,类似于单行本的两条行线。设置该值,能将文本上下平均的放在这个框内,也就是说文本中线和这个文本框的水平中线对齐。

      • 如果文字大小大于该值,超过部分会无法显示;
      • 如果文字大小小于该值,那么文本的上下边缘离这个文本框的上下边的距离是相等的。

      所以,一般可以通过将文字的line-height​​设置为外部盒子的高度一样来实现居中:

      .box{
        height: 40px;
      }
      .text{
        font-size: 18px;
        line-height: 48px;
      }
      
    • vertical-align​​

      这个属性的作用是通过参照baseline​​、外部盒子元素的顶部和底部的位置对元素位置进行调整的,查看可用值: image-20230817155811-0n380ng.png

      通常,这个属性可以用于结局图标和文字的对齐问题。因为图标的基线和文本的基线是一样的,但是图标底边缘不会比基线低一点,而是刚好对齐基线,所以经常会出现下图的微小参差: image-20230817160301-yczgpxe.png

      使用vertical-align​​可以就可以解决该问题:

      .icon{
        vertical-align: sub;
      }
      

      效果如下图: image-20230817160456-mkx8p01.png

    其他的一些可以用于调整垂直居中的方法:

    • 绝对定位:

      .element {
          position: absolute;
          top: 50%;
          transform: translateY(-50%);
      }
      
    • 弹性盒子:

      .box {
          display: flex;
          align-items: center;
      }
      
    • 表格布局:

      .box {
          display: table;
      }
      
      .text {
          display: table-cell;
          vertical-align: middle;
      }
      
    • 网格布局:

      .container {
          display: grid;
          place-items: center;
      }
      

2.3 文本超出省略号替换

  1. 需求描述:

    需要实现下图中对活动标题进行宽度的限制,超出部分用省略号替代: image-20230922212313-nld30jy.png

  2. 解决方案:

    css中就已经提供了相应的属性,以防忘记,再记一遍:

    /* 单行文字换行 */
    .title {
       width: 200px;   /* 对文本的宽度进行限制 */ 
       overflow: hidden; /* 超出的文本隐藏 */
       text-overflow: ellipsis; /* 溢出用省略号显示 */ 
       white-space: nowrap; /* 溢出不换行 */
    }
    /* 多行文字换行 */
    .text-box {
       width: 100px;  /* 限制高度 */
       display: -webkit-box;   /* 伸缩盒模型 */
       -webkit-box-orient:vertical; 
       -webkit-line-clamp:3;   /* 要显示的行数 */
       overflow:hidden;
    }
    

2.4 短边框样式

  1. 需求描述:

    对多宫格存在给宫格项之间添加线条间隔的需求,但间隔的长度小于宫格项的高度时使用border​​时无法实现的。如下图:

    image-20230818152939-ybl3jvb.png

  2. 实现方案:

    通常使用伪元素::before/::after​​实现:

    <t-row>
      <t-col span="8" class="colTitle selectedColTitle">最新活动</t-col>
      <t-col span="8" class="colTitle">高分活动</t-col>
      <t-col span="8" class="colTitle"><t-icon name="filter" size="18px" />筛选</t-col>
    </t-row>
    
    /* 普通样式 */
    .colTitle {
      color: #000000e6;
      font-size: 14px;
      font-weight: 400;
      font-family: "PingFang SC";
      height: 48px;
      line-height: 48px;
      text-align: center;
      border-bottom: 0.5px solid #e7e7e7;
    }
    .selectedColTitle {
      color: #0052d9;
      font-size: 14px;
      font-weight: 600;
    }
    
    /* 伪元素竖边框 */
    .t-row{
      position: relative;
    }
    .colTitle:nth-child(3)::before {
      content: '';
      width: 1px;
      height: 22px;
      background-color: #e7e7e7;
      position: absolute;
      right: 33%;
      top: 50%;
      transform: translateY(-50%);
    }
    

    注意此处使用绝对定位将伪元素相对外部容器t-row​​绝对定位,便于将边框调整在合适的位置。使用百分比的数值单位,而不是具体数据,当外部容器的宽高改变的时候不会使布局产生较大的偏差。

2.5 动态样式

  1. 需求描述:

    在轮播图的图片列表中,只有当前正在展示的图片需要添加阴影的样式: image-20230818154349-25u6glv.png

  2. 实现方案:

    由于我使用的是第三方组件库,所以首先需要分析该轮播图组件是如何标注正在展示的图片是哪个的,通常是绑定元素的某个属性或者class: image-20230818154641-8a6xm0p.png

    滑动图片的时候发现<swiper>​​的current​​属性值在变化,同时<swiper-item>​​的aria-hidden​​在变化,false​​表示正在展示的图片,反之为隐藏图片。

    可以使用属性选择器来实现:

    .t-swiper__item {
      height: 217px !important;
      transition: box-shadow 1s ease;  /* 使用transition实现过渡效果 */
    }
    
    .t-swiper__item[aria-hidden="false"] .t-image {
      box-shadow: 0 0 20px #000;
    }