Skip to content

uni-app 生命周期与适配(四) ​

本页对应 uni-app 生命周期与适配,共 6 段视频,约 56 分钟。

学习目标 ​

  1. 能使用安全区域变量为 custom-stickyy 设置顶部偏移,并通过负 margin 抵消 padding 造成的滚动位置异常。
  2. 能说明小程序自定义组件样式隔离的默认表现,并通过选项式 options 将 styleIsolation 设为 shared,验证页面样式可影响组件样式。
  3. 能使用 v-model 获取 uni-forms 中各表单项数据,并通过 :model 将整体表单数据传给 uni-forms。
  4. 能为 uni-forms-item 设置与数据字段对应的 name,并定义包含 required、pattern、errorMessage 的 :rules 验证规则。
  5. 能通过 ref 获取表单组件实例,在提交按钮事件中调用 validate() 触发验证,并观察必填与格式错误提示。
  6. 能区分 uni-app 中应用、页面、组件三类生命周期,并按页面用小程序生命周期、组件用 Vue 生命周期、应用用应用级生命周期的原则正确选用。

知识点 ​

自定义 sticky 组件:先解决“太靠上”,再处理“盒子变大” ​

为什么需要:自定义 sticky 组件在页面滚动时吸顶,但吸顶位置不能随便写死。写 10px 可能太靠上,写 40px 在小程序里看着正常,到网页里又不协调。
怎么想:吸顶距离应取“顶部安全距离”,而不是固定值;但用 padding-top 撑开距离会让盒子整体变大,滚动前后表现不一致。
怎么做:转写稿中的调试过程是:先给 padding-top: 10px,发现太靠上;改成 40px 后小程序稍正常,但网页不协调;于是改为通过安全区域/API 获取顶部距离,再绑定到样式变量上,并记得加单位 px。随后审查元素发现:滚动到顶部时正常,不滚动时距离偏大,是因为上边多了一层 padding。解决思路是给 sticky 外层再套一个父盒子,用负 margin-top 抵消向下的 padding。
易错点:padding 不能写负值,margin 可以写负值;正负值要抵消一致;转写稿中加父盒子后 sticky 一度失效,明确说“先留一个小 bug,不继续抠”,所以这里不要臆造最终修复方案。

css
/* 示意:用负 margin 抵消 padding 造成的下移 */
.sticky-wrap {
  margin-top: -20px;
}

样式隔离:为什么网页能改,小程序改不动组件样式 ​

为什么需要:把页面里的一部分表单抽成组件后,页面样式可能突然失效。转写稿中把表单抽成 custom-form 组件,网页里用类名改小眼睛颜色、去掉边框都生效,但小程序里不生效。
怎么想:这不是普通 CSS 优先级问题,而是小程序自定义组件默认有样式隔离。查文档可知 styleIsolation 默认是 isolated,页面样式默认不影响组件内部;改成 shared 后,页面与组件样式才可互相影响。
怎么做:在使用组件的页面/组件里加选项式配置:

js
export default {
  options: {
    styleIsolation: 'shared'
  }
}

转写稿中强调:组件里再套组件时,要改样式也要注意把对应组件的样式隔离设为 shared。
易错点:这是小程序侧问题,网页端不一定复现;不要把它当成选择器写错;改的是“使用组件处”的配置,不只是组件内部随便加一行。

uni-forms 第一步:先用 v-model 拿到表单数据 ​

为什么需要:验证前必须拿到用户输入。原生小程序双向绑定很麻烦,而 uni-app 中 v-model 可以直接用。
怎么想:每个输入框绑定 formData 里的字段;整个表单再把 formData 通过 :model 传给 uni-forms。
怎么做:

vue
<uni-forms :model="formData">
  <uni-forms-item name="mobile">
    <uni-easyinput v-model="formData.mobile" />
  </uni-forms-item>
  <uni-forms-item name="password">
    <uni-easyinput v-model="formData.password" />
  </uni-forms-item>
</uni-forms>
js
const formData = ref({
  mobile: '',
  password: ''
})

易错点:v-model 负责字段数据,:model 负责把整份数据交给表单组件,两者别混;转写稿中调试时页面更新有时“反应迟钝”,但刷新后能看到数据已变化,不代表绑定失败。

uni-forms 第二步:定义 rules,并让 name 与字段对应 ​

为什么需要:不同字段规则不同,手机号要求 11 位,密码可能要求字母数字下划线,必须把规则传给表单。
怎么想:rules 是数组,每个对象代表一个字段的规则;对象里的 name 要和 uni-forms-item 的 name 对应。规则里常用 required、message、pattern。
怎么做:

js
const rules = {
  mobile: {
    rules: [
      { required: true, message: '用户名不能为空' },
      { pattern: /^1\d{10}$/, message: '不符合手机号规则' }
    ]
  },
  password: {
    rules: [
      { required: true, message: '密码不能为空' },
      { pattern: /^\w+$/, message: '只能字母数字下划线' }
    ]
  }
}

模板上补 :rules="rules"。
易错点:转写稿特别提醒正则里的 \d 要注意转义,按实际字符串写法可能需要多写一个斜杠;name 不匹配时规则找不到对应字段。

uni-forms 第三步:点击按钮触发 validate ​

为什么需要:规则写完不会自动生效,通常在提交按钮点击时统一校验。
怎么想:先通过 ref 拿到表单组件实例,再调用实例的 validate()。
怎么做:

vue
<uni-forms ref="formRef" :model="formData" :rules="rules">
  <!-- 表单项 -->
</uni-forms>
<button @click="onSubmit">提交</button>
js
const formRef = ref(null)

const onSubmit = () => {
  formRef.value.validate()
}

易错点:ref="formRef" 这里没有冒号;忘记把 :rules 绑到表单上,规则不会参与校验;转写稿调试时清空字段、输入不足 11 位,分别出现了“不能为空”和“不符合手机号规则”的提示。

生命周期:三类并存,按“页面用小程序、组件用 Vue”来选 ​

为什么需要:uni-app 同时有 Vue 和小程序能力,生命周期容易用混。
怎么想:应用级用 onLaunch/onShow/onHide;页面级建议用小程序生命周期,如 onLoad/onShow/onReady/onHide;自定义组件里用 Vue 生命周期,如 mounted/beforeMounted。转写稿明确说:Vue 生命周期在页面里“可以用”,但为了避免混乱,建议页面不要用;onLoad 不能写进组件。
怎么做:页面里写 onLoad 等;组件里写 mounted 等;App 里写应用级生命周期。
易错点:不要因为“能用”就混着用;组件里写 onLoad 不生效;本节只要求知道三类生命周期的存在和选用原则,不需要死背表格。

一、从 sticky 安全区踩坑看“样式为什么要隔离” ​

为什么需要:课堂演示 custom-sticky 时,顶部吸附距离一开始写死 10px,网页里勉强正常,小程序里却太靠上;改成 40px 后小程序好一点,网页又不协调。说明固定值不能同时适配不同端和安全区域。

怎么想:既然不能写死,就应该用安全区域距离。转写稿里用类似 env(safe-area-inset-top) 的方式拼接,而不是直接写 10px 或 40px。但一用 padding-top,盒子会被撑大,滚动前后表现又不一致;于是又想到给父级加负 margin-top 抵消,所谓“一正一负就抵消”。

怎么做:思路是“安全区 padding + 父级负 margin 抵消”。示意代码:

css
.sticky {
  padding-top: calc(10px + env(safe-area-inset-top));
}
.sticky-wrapper {
  margin-top: -10px; /* 具体值要与 padding 抵消,课堂中曾用负值演示 */
}

易错点:padding 会改变盒子尺寸,margin 可以为负、padding 不能为负;课堂里加完父级负 margin 后,sticky 一度失效,明确说“先留一个小 bug,后面再查”,所以这里不能把“负 margin”当成万能解。调试时要同时看小程序和网页,不能只看一端。

二、自定义组件样式隔离:页面类名为什么改不动组件 ​

为什么需要:课堂把表单相关结构抽成 custom-form 组件,页面里只是引入组件,按理说“只是抽离,不应该有影响”。但页面里用类名改小眼睛颜色、去边框线时,网页生效,小程序不生效。原因就是小程序自定义组件默认有样式隔离:组件外定义的类名不会影响组件内部。

怎么想:先确认不是选择器写错。转写稿里审查元素,看到小眼睛类名,页面 CSS 也写了颜色,网页确实变红;到小程序却不生效。于是转到 styleIsolation:默认隔离时,页面样式影响不到组件;把值改成 shared 后,页面和组件样式可以互相影响。

怎么做:在使用组件的页面/组件配置里加选项式 options。转写稿说“两个地方都加一下,把组件里也加上”,核心代码是:

js
export default {
  options: {
    styleIsolation: 'shared'
  }
}

易错点:shared 不要拼错;要加在“使用组件”的页面或组件配置里,不是随便写在 CSS 里;组合式 API 与选项式 options 可以共存;这是小程序端问题,网页端不一定复现。改完还要等编译完成再判断,课堂里就遇到编译慢、报“组件不存在”的情况,不一定是代码错。

三、顺带收束:表单验证与生命周期的固定套路 ​

为什么需要:表单验证是为了拿到用户输入并按规则提示;生命周期是为了知道代码该写在页面还是组件里。

怎么想:表单验证分三步:先拿数据,再定规则,最后触发验证。拿数据用 v-model,并把整体数据通过 :model 交给 uni-forms;规则用 rules,靠 name 与表单字段对应;触发时用 ref 拿表单实例,调用 validate()。

怎么做:关键代码示意:

vue
<uni-forms ref="formRef" :model="formData" :rules="formRules">
  <uni-forms-item name="mobile" label="手机号">
    <uni-easyinput v-model="formData.mobile" />
  </uni-forms-item>
  <uni-forms-item name="password" label="密码">
    <uni-easyinput v-model="formData.password" />
  </uni-forms-item>
</uni-forms>
<button @click="submit">提交</button>
js
const formRef = ref()
const formData = ref({ mobile: '', password: '' })
const formRules = {
  mobile: {
    rules: [
      { required: true, errorMessage: '用户名不能为空' },
      { pattern: /^1\d{10}$/, errorMessage: '不符合手机号规则' }
    ]
  },
  password: {
    rules: [
      { required: true, errorMessage: '密码不能为空' },
      { pattern: /^\w+$/, errorMessage: '只能是字母数字下划线' }
    ]
  }
}
const submit = () => {
  formRef.value.validate()
}

易错点:name 要和 rules 的键、数据字段对应;:model 不要漏;正则写在字符串里要注意转义,写在字面量里按 JS 正则写;ref 绑定不要写成 :ref。生命周期方面,转写稿给的原则是:应用级、页面级以小程序生命周期为准,自定义组件以 Vue 生命周期为准;页面里虽然能用 Vue 生命周期,但为了避免混用,建议页面用 onLoad、onShow 等,组件用 mounted 等。

先明确目标:表单验证要解决什么问题 ​

这一节的核心不是“会写一个输入框”,而是让用户填写的数据能够被拿到、被规则校验、被提交时触发验证。转写中把需求拆得很清楚:对表单里的手机号、密码等数据做验证。
为什么需要:如果不验证,空内容、位数不对、格式不对的数据都会进入后续业务。
怎么想:uni-forms 的验证是固定套路,分三步——先获取表单数据,再定义验证规则,最后触发验证。
怎么做:围绕 uni-forms、uni-forms-item、v-model、:model、:rules、validate() 展开。
易错点:不要一上来就写规则,先把数据和表单组件关联起来;否则规则写了也不生效。

第一步:获取表单数据 ​

为什么需要:验证的前提是能拿到用户在输入框里填写的内容。转写中反复强调:用户在表单里写 123,我们要能拿到 123;写 ABC,要能拿到 ABC。
怎么想:每个输入框用 v-model 绑定到响应式数据上;同时把整个表单数据通过 :model 传给 uni-forms。这和 Element UI 的表单用法很像。
怎么做:先定义表单数据,再在模板中绑定。示例按转写中的思路整理:

vue
<script setup>
import { ref } from 'vue'

const formData = ref({
  mobile: '',
  password: ''
})
</script>

<template>
  <uni-forms :model="formData">
    <uni-forms-item name="mobile">
      <uni-easyinput v-model="formData.mobile" />
    </uni-forms-item>

    <uni-forms-item name="password">
      <uni-easyinput v-model="formData.password" />
    </uni-forms-item>
  </uni-forms>
</template>

转写中调试时就是改输入框内容,观察 formData 是否变化;虽然有时页面反应慢一点,但双向绑定是生效的。
易错点:只写 v-model 不够,还要在 uni-forms 上写 :model="formData",把整体数据交给表单组件。v-model 前没有冒号,:model 前有冒号,不要混。

第二步:定义验证规则 ​

为什么需要:不同字段规则不同。手机号要求 11 位、以 1 开头;密码可能要求不能为空、只能字母数字下划线。规则必须能区分“验证哪个字段”。
怎么想:rules 是一个对象/数组结构,里面的键要和 uni-forms-item 的 name 对应。转写中强调:form-item 加 name,最好和数据名一致,比如 mobile 对应 mobile,这样规则才能找到字段。
怎么做:定义 formRules,并通过 :rules 传给 uni-forms;每个字段里可写 required、message、pattern。

vue
<script setup>
import { ref } from 'vue'

const formData = ref({
  mobile: '',
  password: ''
})

const formRules = {
  mobile: [
    { required: true, message: '用户名不能为空' },
    { pattern: /^1\d{10}$/, message: '不符合手机号规则' }
  ],
  password: [
    { required: true, message: '密码不能为空' },
    { pattern: /^[A-Za-z0-9_]+$/, message: '只能字母数字下划线' }
  ]
}
</script>

<template>
  <uni-forms :model="formData" :rules="formRules">
    <uni-forms-item name="mobile">
      <uni-easyinput v-model="formData.mobile" />
    </uni-forms-item>
    <uni-forms-item name="password">
      <uni-easyinput v-model="formData.password" />
    </uni-forms-item>
  </uni-forms>
</template>

易错点:name 和 rules 的键不一致,验证不会按预期找到字段;如果正则写成字符串形式,反斜杠要注意转义,转写中特别提醒 \d 这类写法在字符串里要多加斜杠。

第三步:触发验证 ​

为什么需要:规则定义好不会自动在提交时校验,需要用户点按钮时调用表单实例的验证方法。
怎么想:uni-forms 组件实例有 validate 方法;先通过 ref 拿到组件实例,再在提交事件里调用。转写中明确:ref 属性不要加冒号。
怎么做:

vue
<script setup>
import { ref } from 'vue'

const formRef = ref(null)
const formData = ref({ mobile: '', password: '' })
const formRules = {
  mobile: [
    { required: true, message: '用户名不能为空' },
    { pattern: /^1\d{10}$/, message: '不符合手机号规则' }
  ],
  password: [
    { required: true, message: '密码不能为空' }
  ]
}

function submit() {
  formRef.value.validate()
}
</script>

<template>
  <uni-forms ref="formRef" :model="formData" :rules="formRules">
    <uni-forms-item name="mobile">
      <uni-easyinput v-model="formData.mobile" />
    </uni-forms-item>
    <uni-forms-item name="password">
      <uni-easyinput v-model="formData.password" />
    </uni-forms-item>
  </uni-forms>
  <button @click="submit">提交</button>
</template>

转写中的调试过程是:清空数据点提交,出现“不能为空”;输入不足 11 位点提交,出现“不符合手机号规则”。说明验证链路已经打通。
易错点:忘记写 :rules;忘记给 uni-forms 加 ref;把 ref="formRef" 写成 :ref="formRef";调用时漏掉 .value 或 validate 拼写错误。

调试与边界提醒 ​

表单验证是固定步骤,建议按“数据绑定 → 规则定义 → 点击触发”的顺序排查。若在自定义组件里改样式不生效,那是小程序的样式隔离问题,不是表单数据问题;转写中通过选项式 options: { styleIsolation: 'shared' } 让页面样式影响组件样式。生命周期部分只记原则:应用和页面生命周期以小程序为准,自定义组件生命周期以 Vue 为准。表单这一节先把三步跑通,再处理样式和生命周期细节。

代码示例 ​

示例 1:自定义组件 custom-sticky(安全区顶部距离 + 负 margin 抵消) ​

vue
<!-- components/custom-sticky/custom-sticky.vue -->
<template>
  <!-- 父盒子:用负 margin 把被 padding 顶下去的盒子拉回来 -->
  <view class="sticky-wrap" :style="{ marginTop: -safeTop + 'px' }">
    <!-- 吸顶盒子:padding-top 撑出安全区距离 -->
    <view class="sticky" :style="{ paddingTop: safeTop + 'px' }">
      <slot></slot>
    </view>
  </view>
</template>

<script setup>
import { ref } from 'vue'

// 动态获取安全区顶部距离,而不是写死 10px / 40px
const safeTop = ref(0)
const info = uni.getSystemInfoSync()
safeTop.value = info.safeAreaInsets ? info.safeAreaInsets.top : info.statusBarHeight
</script>

<style scoped>
.sticky {
  position: sticky;
  top: 0;
  background-color: #fff;
  z-index: 99;
}
</style>

关键行与易错点

  • :style="{ paddingTop: safeTop + 'px' }":必须用变量而不是写死 10px/40px,否则小程序里正常、H5 里不协调;单位 px 不能丢。
  • :style="{ marginTop: -safeTop + 'px' }":padding 会让盒子整体变大,滚动到顶部时距离显得过大。给外层盒子一个负 margin 抵消,正负必须用同一个变量,写死 -20px 就对不上了。
  • margin 可以给负值,padding 不能给负值——这是选负 margin 的原因。
  • 加了外层包裹盒子后,position: sticky 的层级/包含块会受影响,可能出现吸顶暂时失效,这是本节遗留的小瑕疵,定位元素与包裹盒子的层级关系要一并检查。
  • uni.getSystemInfoSync() 取不到 safeAreaInsets 时要有 statusBarHeight 兜底。

示例 2:自定义组件样式隔离(styleIsolation: 'shared') ​

vue
<!-- pages/notify/index.vue -->
<template>
  <view class="page">
    <custom-form />
  </view>
</template>

<!-- 选项式:专门为小程序样式隔离而写 -->
<script>
export default {
  options: {
    styleIsolation: 'shared'
  }
}
</script>

<!-- 组合式逻辑照常写,两者可以同时存在 -->
<script setup>
import CustomForm from '@/components/custom-form/custom-form.vue'
</script>

<style>
/* 页面样式要穿透进组件,改组件内部类名 */
.field-icon {
  color: red !important;
}
</style>
vue
<!-- components/custom-form/custom-form.vue -->
<script>
export default {
  options: {
    styleIsolation: 'shared'
  }
}
</script>

<script setup>
// 组件内部逻辑
</script>

关键行与易错点

  • 小程序默认样式隔离(isolated),页面里写的类名影响不到组件内部,所以 color: red、边框线在 H5 生效、在小程序不生效。
  • 解决方式:在使用组件的页面和组件本身都加 options: { styleIsolation: 'shared' }(转写稿强调“两个地方都加一下”),共享后页面样式才能影响组件。
  • options 是选项式写法,和 <script setup> 组合式可以共存,写在单独的 <script> 块里。
  • 这个 bug 只在小程序端出现,H5 端不需要处理,别在 H5 上白折腾。
  • 用 !important 提优先级,是为了盖住组件内部的默认样式。

示例 3:uniForms 表单数据 + 验证规则 + 触发验证 ​

vue
<!-- components/custom-form/custom-form.vue -->
<template>
  <view>
    <uni-forms ref="formRef" :model="formData" :rules="formRules">
      <uni-forms-item label="手机号" name="mobile">
        <uni-easyinput
          v-model="formData.mobile"
          :inputBorder="false"
          placeholder="请输入手机号"
        />
      </uni-forms-item>

      <uni-forms-item label="密码" name="password">
        <uni-easyinput
          v-model="formData.password"
          type="password"
          :inputBorder="false"
          placeholder="请输入密码"
        />
      </uni-forms-item>
    </uni-forms>

    <button @click="onSubmit">提交</button>
  </view>
</template>

<script setup>
import { ref } from 'vue'

// 1. 表单数据
const formData = ref({
  mobile: '',
  password: ''
})

// 2. 验证规则
const formRules = {
  mobile: {
    rules: [
      { required: true, errorMessage: '用户名不能为空' },
      { pattern: '^1\\d{10}$', errorMessage: '不符合手机号的规则' }
    ]
  },
  password: {
    rules: [
      { required: true, errorMessage: '密码不能为空' },
      { pattern: '^\\w+$', errorMessage: '只能是字母数字下划线' }
    ]
  }
}

// 组件实例
const formRef = ref(null)

// 3. 触发验证
const onSubmit = () => {
  formRef.value.validate()
    .then(() => {
      console.log('校验通过', formData.value)
    })
    .catch((err) => {
      console.log('校验失败', err)
    })
}
</script>

关键行与易错点

  • 第一步取数据:三个 uni-easyinput 各写一个 v-model(formData.mobile、formData.password),小程序原生没有真正的双向绑定,uni-easyinput 里可以直接用。
  • 必须再给 uni-forms 加 :model="formData",把整体数据交给表单组件,否则校验无从下手。
  • uni-forms-item 的 name 要和数据字段名一致(都叫 mobile / password),它决定这条规则校验的是哪个数据。
  • formRules 的写法是“对象 → rules 数组 → 规则对象”,一个字段可以有多条规则(required + pattern);它不参与响应式,用普通对象即可。
  • 正则写成字符串时要多加一层转义:'^1\\d{10}$',少写一个斜杠 \d 就失效。
  • ref="formRef" 上不能加冒号,加了就成了传值而不是拿实例;拿到实例后调用 validate(),返回 Promise,用 then/catch 区分通过和失败。

常见错误 ​

  1. 现象:custom-sticky 吸顶偏移写死 10px/40px,小程序看着正常,网页不协调。→ 原因:没有按安全区动态取值。→ 修法:用安全区 API 取顶部距离,变量拼接单位后绑定,不写死数值。

  2. 现象:给 sticky 外层加负 margin-top 抵消 padding-top 后,吸顶失效或层级异常。→ 原因:负 margin 改变了盒模型/层叠关系,sticky 受父级和层级影响。→ 修法:先回归验证 sticky 自身定位、父级 overflow/transform 和层级;负 margin 只做视觉补偿,不能把失效 bug 留到提交。

  3. 现象:网页里改组件内“小眼睛”颜色、边框线生效,小程序不生效。→ 原因:小程序默认样式隔离,页面样式影响不到组件内部。→ 修法:在组件使用侧或组件选项中设置 styleIsolation: 'shared',再检查选择器和优先级。

  4. 现象:uniForms 写了 rules,点提交没有任何校验提示。→ 原因:规则没有通过 :rules 绑定到 form 组件。→ 修法:在 form 上补 :rules="formRules",确认规则对象与 form 已关联。

  5. 现象:校验提示错字段或不触发。→ 原因:form-item 的 name 与 formData 字段名不一致。→ 修法:name 与数据字段严格对齐,如 mobile、password。

  6. 现象:提交时报 validate is not a function 或无反应。→ 原因:ref 写法错误或没拿到 form 组件实例。→ 修法:模板写 ref="formRef",不要加冒号;脚本定义 const formRef = ref();提交时调用 formRef.value.validate()。

  7. 现象:手机号正则复制后规则异常。→ 原因:字符串里的 \d 没有转义。→ 修法:写成 \\d,或改用 RegExp 构造,并实际输入 11 位/非 11 位验证。

练习 ​

  1. 把 sticky 吸顶偏移从写死 40px 改为安全区自适应,并分别在小程序和网页验证滚动前后表现。
  2. 排查“网页改组件内小眼睛生效、小程序不生效”的完整步骤,给出最小修改方案。
  3. 补全一个 uniForms 表单:三个输入框加 v-model,form 加 :model,两个 form-item 的 name 对齐字段,配置必填和手机号/密码正则,提交按钮触发 validate。
  4. 改错:模板里出现 :ref="formRef"、form 未绑定 :rules、form-item 的 name 与数据字段不一致。逐条指出问题并修正。
  5. 生命周期选型:页面 onShow、组件 mounted、App onLaunch 分别应写在哪里;并说明组件里写 onLoad 为什么无效。

课堂画面 ​

课堂画面 01

课堂画面 02

课堂画面 03

课堂画面 04

课堂画面 05

课堂画面 06

原视频对照 ​

#视频时长
1自定义组件custom-stickyy应用09:00
2自定义组件样式隔离14:50
3uniForms表单数据08:20
4uniForms表单验证规则09:35
5uniForms触发验证07:44
6uni-app生命周期06:47

小结 ​

  1. sticky 偏移不要写死,安全区变量加单位;负 margin 补偿后必须回归吸顶和层级。
  2. 小程序跨组件改样式,先处理样式隔离,再谈选择器优先级。
  3. uniForms 验证是一条固定链路:取数、汇总、对齐 name、绑定 rules、调用 validate,缺一环就会“不提示/错字段”。
  4. ref 获取组件实例不加冒号;validate 是组件实例方法,调用前先确认实例存在。
  5. 生命周期选型原则:应用/页面以小程序为准,自定义组件以 Vue 为准;页面别混用 Vue 生命周期,组件别写 onLoad。
  6. 表单调试顺序:先看数据是否更新,再看 rules 是否绑定,再看 name 是否匹配,最后看 validate 是否调用。
  7. sticky 的小 bug 不要“先放着”,要定位到负 margin、层叠上下文或父级限制,否则后面联调成本更高。