Skip to content

生命周期、分包与自定义组件(一) ​

本页对应 生命周期、分包与自定义组件,共 8 段视频,约 1 小时 43 分。

学习目标 ​

  • 能够在 app.js 中使用 App() 注册应用,并演示 onLaunch、onShow、onHide 的触发时机,通过 onLaunch 的 options 获取场景值与启动参数。
  • 能够在小程序启动时调用更新管理器检查版本更新,并使用模态弹窗提示用户,用户确认后应用更新并重启。
  • 能够在 app.json 中配置 subPackages 的 root 与 pages 完成分包,并将指定页面和资源划入分包,说明主包体积限制及 tabBar 页面不能放入分包的规则。
  • 能够配置 preloadRule 实现分包预下载,设置页面路径、网络类型和预下载分包,并验证控制台出现预加载成功提示。
  • 能够创建自定义组件(component: true、Component()),通过 usingComponents 完成全局或局部注册并在页面中复用。
  • 能够通过 globalClass 或 externalClasses 修改自定义组件样式,并使用 navigationStyle: custom 实现自定义导航栏及标题居中布局。

知识点 ​

分包加载要解决的核心问题 ​

为什么需要:微信小程序对单个包体积有硬限制,主包超过 2M 会影响预览、上传和上线;即使没超,也可以把非首屏页面拆出去,让首次启动只加载主包。转写稿里导入第三天素材时,项目体积约 3.3M,点击上传直接报错:当前体积 3328KB,超过最大限制 2M。这说明“代码体积”不是抽象概念,而是会卡住发布的真实问题。

怎么想:先判断哪些内容属于首屏必须加载,哪些属于二级页面、低频模块。比如房屋列表、详情、报修等可以按业务模块拆到分包;大图片如果确实必须本地存放,也可以随分包放,避免压在主包里。配置文件中能忽略的通常只是静态资源,代码本身不能靠忽略解决。

怎么做:在全局配置里用 subPackages 声明分包根目录和页面路径:

json
{
  "subPackages": [
    {
      "root": "subpkg_test",
      "pages": [
        "pages/test/index"
      ]
    },
    {
      "root": "subpkg_user",
      "pages": [
        "pages/profile/index"
      ]
    }
  ],
}

root 是分包根目录,pages 是分包内页面路径。根目录和页面不存在时,开发者工具会自动创建。配置保存后,工具会按路由生成对应页面文件。

易错点:严格 JSON 不能写注释、不能用单引号,末尾逗号也要删掉;上面示例若直接使用,需要去掉最后一个逗号。tabBar 页面不能放进分包,只能留在主包。分包不是无限拆,转写稿提到所有分包总体积也不能超过 20M;主包体积最好控制在 1.5M 以内,否则首启速度仍会受影响。

把页面和资源一起拆出去 ​

为什么需要:只拆页面但不拆大资源,主包仍可能超限。转写稿中通过“详情 → 依赖分析”发现 picture-02 单张图就接近 1M,几张图叠加就把主包撑大了。

怎么想:按业务模块划分分包,例如 packages/house、packages/repair;模块内页面和该模块才用到的图片、静态资源放在同一分包目录。主包只保留首页、tabBar 页面和公共资源。

怎么做:配置 root 后,在分包目录下放 static 等资源目录,把大图从主包移到分包;主包页面跳转分包页面时,路径仍按根目录写法,如 /packages/house/pages/list。转写稿演示移动两张图后,主包体积下降,预览二维码重新出现,说明上传成功。

易错点:分包之间资源相互独立,不要跨分包互相引用;分包页面路径要和 root 拼接正确。转写稿还提醒,主包与分包资源引用有边界,配置时以官方文档为准,避免把公共资源散落到各分包。

分包预加载:把“即将访问”的包提前下载 ​

为什么需要:纯按需加载是用户点进分包页面才开始下载,网络慢时会有等待。预加载可以在进入某个主包页面时,提前下载目标分包,等真正跳转时直接打开。

怎么想:找出用户下一步高概率访问的页面,例如从框架页进入个人中心,就在框架页配置预加载 subpkg_user。不是所有包都预加载,否则会浪费流量、拖慢当前页面。

怎么做:在全局配置中用 preloadRule:

json
{
  "preloadRule": {
    "pages/framework/index": {
      "network": "all",
      "packages": ["subpkg_user"]
    }
  },
}

含义是加载 pages/framework/index 时自动加载 subpkg_user 分包。network 默认通常是 Wi-Fi,all 表示所有网络都预加载;packages 是数组,可以配多个分包。转写稿演示配置后在控制台看到预加载成功提示。

易错点:preloadRule 与 subPackages 同级;key 是页面路径,value 里写网络和包名;严格 JSON 同样不能有末尾逗号。预加载只是优化,不能替代分包配置;未配置预加载时,仍是跳转后才开始加载。调试时可先看控制台是否有预加载成功/失败提示,再检查页面路径和分包 root 是否写错。

基本语法:组件不是页面,先分清入口 ​

为什么需要:页面中的搜索框、导航栏等结构如果反复写,会重复且难维护;把它们抽成自定义组件,才能复用和隔离逻辑。怎么想:组件结构仍由 .wxml、.wxss、.js、.json 构成,但必须让工具识别“这是组件”。怎么做:课堂演示中通过开发者工具右键新建 Component;关键区别是 .json 必须有 component: true,.js 调用 Component 而不是页面的 Page。使用时在页面或全局注册,配置项为 usingComponents,属性名是组件名,值是路径。

json
{
  "usingComponents": {
    
    "自定义组件名称": "自定义组件的路径",
    "navigation-bar": "./navigation-bar/index"
  }
}

易错点:转写稿中特意对比了新建 Page 与 Component:Page 的入口是 Page,Component 的入口是 Component;新建 Page 还会自动注册路由,删页面时要手动删路由。全局注册写在 app.json,局部注册写在页面 .json;全局路径常以 / 开头,局部可用 ./ 相对路径。

组件样式:默认隔离,修改要有“入口” ​

为什么需要:组件封装后,页面样式不应随便污染组件内部;但业务又常要改组件外观。怎么想:小程序页面和组件样式默认隔离,所以页面直接写组件内部类名通常不生效。怎么做:方案一在组件 Component 中设置 options.addGlobalClass: true,允许页面样式影响组件,但页面必须使用组件本身的类名;方案二用 externalClasses 声明外部样式类,把父级类名传入组件结构。

javascript
Component({
  options: {
    addGlobalClass: true,
  },
  // 开发自定义的样式类
  externalClasses: ['custom-class', 'title-class']
})
xml
<navigation-bar
  custom-class="my-navigation-bar" 
  title-class="my-navigation-bar-title"
></navigation-bar>
xml
<view class="navigation-bar custom-class">
  <view class="navigation-bar-title title-class">
    自定义标题
  </view>
</view>

易错点:转写稿调试时,页面改组件背景色不生效,开启 addGlobalClass 后生效但被组件内部样式权重覆盖,需要提高权重(如 !important)。组件内定义样式不要用标签选择器、ID 选择器或属性选择器;标签选择器会影响页面同类标签。外部样式类可理解为“特殊 props”:先在 externalClasses 声明坑位,再在组件结构类名中占位,父级传类名进来。

slot:先占位,再决定单槽还是多槽 ​

为什么需要:组件结构固定、内容可变时,需要把父级内容塞进组件内部。怎么想:<slot /> 就是占位;默认只能一个,多个插槽要开启多插槽并用 name 区分。怎么做:组件内写 <slot />;多插槽设置 options: { multipleSlots: true },不同 <slot name="title" /> 用 name 区分;使用时单插槽直接在组件中间填内容,多插槽用 slot 属性指定位置。易错点:忘记开启 multipleSlots 却写多个插槽;使用多插槽时没写 slot 属性导致内容不知道落到哪。

生命周期:created 与 attached 的时机 ​

为什么需要:组件创建、初始化完成时需要执行不同逻辑。怎么想:生命周期写在 lifetimes 中,不要和旧写法混淆。怎么做:

javascript
Component({
  lifetimes: {
    created() {
      // code...
    },
    attached() {
      // code...
    }
  }
})

易错点:created 类似 Vue 的 created,组件创建时触发,不能调用 this.setData;attached 类似 mounted,组件初始化完毕时触发,实际开发使用最频繁。

组件通信:properties 收数据,triggerEvent 发事件 ​

为什么需要:父页面要把数据传给子组件,子组件也要把状态变化通知外部。怎么想:父传子用自定义属性 properties;子传父用自定义事件。怎么做:

javascript
Component({
  properties: {
    back: Boolean,
    delta: {
      type: Number,
      value: 1
    }
  }
})
xml
<navigation-bar back delta="1">
  <text slot="title">自定义标题</text>
</navigation-bar>

事件监听写 bind:事件类型="事件回调",触发写 this.triggerEvent('事件类型', 参数)。倒计时示例中,attached 调 countStart,每次 setData 后 triggerEvent('change', this.data.time) 把剩余时间传出。易错点:properties 的 type/value 要写清;事件名要和 bind: 后一致;定时器逻辑注意终止条件。

css
.navigation-bar {
  left: 0;
  top: 0;
  z-index: 999;
  display: flex;
  align-items: center;
  height: 44px;
  background-color: #fff;
}

.navigation-bar-title {
  flex: 1;
  text-align: center;
  font-size: 14px;
}

.navigation-bar-back {
  position: absolute;
  left: 10px;
  width: 44rpx;
  height: 44rpx;
  background-image: url(https://lotjol.github.io/static-store/enjoy-plus/images/back-arrow.svg);
  background-repeat: no-repeat;
}
javascript
Component({
  externalClasses: ['custom-class'], // 外部样式类
  properties: {
    time: {
      type: Number,
      value: 60,
    },
    useSlot: Boolean,
  },
  lifetimes: {
    attached() {
      // 开始倒计时
      this.countStart()
    },
  },
  methods: {
    countStart() {
      let timer = null
      // 停止定时器
      if (this.data.time === 0) return clearTimeout(timer)
      // 更新渲染
      this.setData({
        time: (this.data.time -= 1),
      })
      // 定时器
      timer = setTimeout(this.countStart.bind(this), 1000)
      // 传递数据到组件外部
      this.triggerEvent('change', this.data.time)
    },
  },
})

Vant 组件库:安装后必须构建 ​

为什么需要:常用 UI 不必从零封装。怎么想:注意 Vant 有 Vue 版和小程序版,别装错。怎么做:根目录安装 npm install @vant/weapp;在小程序开发者工具中构建 npm,生成 miniprogram_npm;移除全局配置 "style": "v2",否则样式受影响;在 app.json 全局注册 Vant 组件,组件路径不以 / 或 ./ 开头。

bash
npm install @vant/weapp

易错点:只安装不构建,组件找不到;忘记移除 "style": "v2";路径写法不符合 Vant 注册要求。示例按钮区域样式保留如下:

css
.buttons {
  display: flex;
  justify-content: space-around;
  padding: 20rpx;
  margin: 20rpx;
  border-radius: 10rpx;
  background-color: #fff;
}

课件图示 ​

课件第6页:多⼀句没有,少⼀句不⾏,用更短时间,教会更实用的技术! ⾼级软件⼈才培训专家 本

课件第12页:多⼀句没有,少⼀句不⾏,用更短时间,教会更实用的技术! ⾼级软件⼈才培训专家 l

课件第14页:多⼀句没有,少⼀句不⾏,用更短时间,教会更实用的技术! ⾼级软件⼈才培训专家 l

课件第3页:多⼀句没有,少⼀句不⾏,用更短时间,教会更实用的技术! ⾼级软件⼈才培训专家 l

代码示例 ​

示例一:分包加载与分包预下载(app.json) ​

json
{
  "pages": [
    "pages/index/index",
    "pages/framework/index"
  ],
  "subPackages": [
    {
      "root": "subpkg_test",
      "pages": [
        "pages/test/index"
      ]
    },
    {
      "root": "subpkg_user",
      "pages": [
        "pages/profile/index"
      ]
    }
  ],
  "preloadRule": {
    "pages/framework/index": {
      "network": "all",
      "packages": ["subpkg_user"]
    }
  }
}

关键行与易错点

  • subPackages(也可写 subpackages)是数组,每一项用 root 指定分包根目录、pages 指定该分包内的页面路径;root 和页面路径不存在时,开发者工具会按配置自动创建目录和文件。
  • 分包页面的真实路径 = root + pages 中的路径,例如 subpkg_user/pages/profile/index;跳转时依旧按完整路径写,和普通页面没有区别。
  • root 不要写成 /subpkg_user 这种以斜杠开头的形式;pages 里也不要再重复写 root。
  • pages 数组(主包)里不能出现分包页面;tabBar 配置的页面必须放在主包,不允许分包。
  • preloadRule 与 subPackages 同级,它的 key 是页面路径,value 中 network 可取 all / wifi(默认 wifi),packages 是数组,可一次预下载多个分包。
  • 主包体积建议不超过 1.5M、单个包不超过 2M、所有分包总和不超过 20M;超出 2M 会直接上传失败(报错信息中会提示 max limit)。
  • 该文件是严格 JSON:不能写注释、键和值必须用双引号、末尾不能有逗号,语法错误会导致整个页面白屏。

示例二:自定义导航栏组件(含样式隔离与外部样式类) ​

components/navigation-bar/index.json

json
{
  "component": true,
  "usingComponents": {}
}

components/navigation-bar/index.js

js
Component({
  options: {
    addGlobalClass: true,      // 允许页面样式影响组件内部
    multipleSlots: true        // 启用多插槽
  },
  // 开发自定义的样式类(外部样式类)
  externalClasses: ['custom-class', 'title-class'],
  properties: {
    back: Boolean,
    delta: {
      type: Number,
      value: 1
    }
  },
  lifetimes: {
    created() {
      // 组件创建时触发,此时不能调用 this.setData
    },
    attached() {
      // 组件初始化完毕时触发,最常用
    }
  },
  methods: {
    onBack() {
      // 点击返回的处理逻辑
    }
  }
})

components/navigation-bar/index.wxml

xml
<view class="navigation-bar custom-class">
  <view wx:if="{{back}}" class="navigation-bar-back" bindtap="onBack"></view>
  <view class="navigation-bar-title title-class">
    <slot name="title">自定义标题</slot>
  </view>
</view>

components/navigation-bar/index.wxss

css
.navigation-bar {
  left: 0;
  top: 0;
  z-index: 999;
  display: flex;
  align-items: center;
  height: 44px;
  background-color: #fff;
}

.navigation-bar-title {
  flex: 1;
  text-align: center;
  font-size: 14px;
}

.navigation-bar-back {
  position: absolute;
  left: 10px;
  width: 44rpx;
  height: 44rpx;
  background-image: url(https://lotjol.github.io/static-store/enjoy-plus/images/back-arrow.svg);
  background-repeat: no-repeat;
}

页面中使用(pages/framework/index.json / .wxml / .wxss)

json
{
  "navigationStyle": "custom",
  "usingComponents": {
    "navigation-bar": "/components/navigation-bar/index"
  }
}
xml
<navigation-bar
  back
  delta="1"
  custom-class="my-navigation-bar"
  title-class="my-navigation-bar-title"
>
  <text slot="title">自定义标题</text>
</navigation-bar>
css
.my-navigation-bar {
  background-color: gold;
}

.my-navigation-bar-title {
  color: #fff !important;
}

关键行与易错点

  • 组件同样是 4 个文件,但有两处必须改:.json 中必须有 "component": true,.js 中调用的是 Component() 而不是 Page()。用工具右键新建时一定要选「新建 Component」,选错成「新建 Page」还会自动往 app.json 里写路由,删文件后要手动删路由。
  • 注册组件用 usingComponents,键是自定义组件名、值是路径;全局注册写在 app.json 中(路径建议以 / 开头),局部注册写在页面 .json 中(可用 ./ 相对路径)。注册后即可当标签使用,无需 import。
  • 干掉默认导航栏用页面配置 "navigationStyle": "custom",它是页面的一级配置,别写成嵌套;一般只按页面关闭,不建议全局关闭。
  • 组件样式默认是隔离的,页面里写类名改不动组件内部样式。两种方案:① options: { addGlobalClass: true },但必须使用组件本身的类名,且权重不够时要加 !important;② externalClasses: ['custom-class', 'title-class'],在组件内把类名当作「坑位」写在 class 上,外部通过同名属性传类名进来。
  • 组件内部写样式时不要用标签选择器、ID 选择器、属性选择器,标签选择器会影响到页面中所有同类标签。
  • 多插槽必须 options: { multipleSlots: true },并且 <slot name="title"> 与使用方的 slot="title" 对应;只有一个插槽时直接在组件中间填充内容即可。
  • created 中不能调用 this.setData;数据初始化完毕后的逻辑放在 attached 中。

示例三:倒计时组件与自定义事件通信 ​

components/count-down/index.json

json
{
  "component": true
}

components/count-down/index.js

js
Component({
  externalClasses: ['custom-class'], // 外部样式类
  properties: {
    time: {
      type: Number,
      value: 60
    },
    useSlot: Boolean
  },
  lifetimes: {
    attached() {
      // 开始倒计时
      this.countStart()
    }
  },
  methods: {
    countStart() {
      let timer = null
      // 停止定时器
      if (this.data.time === 0) return clearTimeout(timer)
      // 更新渲染
      this.setData({
        time: (this.data.time -= 1)
      })
      // 定时器
      timer = setTimeout(this.countStart.bind(this), 1000)
      // 传递数据到组件外部
      this.triggerEvent('change', this.data.time)
    }
  }
})

components/count-down/index.wxml

xml
<view class="count-down custom-class">
  <slot wx:if="{{useSlot}}"></slot>
  <text wx:else>{{time}}s</text>
</view>

页面中使用

json
{
  "usingComponents": {
    "count-down": "/components/count-down/index"
  }
}
xml
<count-down time="10" custom-class="my-count-down" bind:change="onTimeChange"></count-down>
js
Page({
  onTimeChange(e) {
    // 自定义事件通过 e.detail 拿到 triggerEvent 传入的参数
    console.log('剩余时间:', e.detail)
  }
})

关键行与易错点

  • 父传子用 properties:back: Boolean 是简写,完整写法是 { type, value };WXML 上传 delta="1" 拿到的是数字类型,由 type: Number 完成转换,不声明 type 会拿到字符串。
  • 子传父用 this.triggerEvent('change', 参数) 触发,父组件用 bind:change="回调" 监听;参数在回调的 e.detail 中,不是 e.currentTarget。
  • 事件名是自定义的,bind: 后面必须与 triggerEvent 的第一个参数完全一致。
  • countStart 中用 setTimeout 递归并 bind(this) 保证 this 指向组件实例;this.data.time === 0 时提前 return 停止计时,否则会无限循环。
  • 外部样式类 custom-class 既要在 externalClasses 中声明,也要在组件 WXML 的 class 中占位,缺一不可,否则外部传入的类名不生效。

常见错误 ​

1. 分包页面跳转报 “Page is not found” 现象:subPackages 里已经配好 packages_house,点击跳转却提示页面不存在。 原因:navigateTo 的 url 只写了 pages/list/index,漏掉了分包的 root。 修法:写全路径 /packages_house/pages/list/index,从开发者工具底部“复制路径”拿到的才是完整路径。

2. 把 tabBar 页面挪进分包后编译报错 现象:配置完分包,工具直接报 tabBar 相关错误。 原因:tabBar 页面必须是主包页面,这是平台限制,不是配置写错。 修法:把该页面移回主包 pages 中,只把二级页面(列表、详情、表单)分出去。

3. 自定义组件被当成页面渲染 / 报 “Component is not found” 现象:组件放进页面后不显示,或者控制台提示找不到组件。 原因:组件的 .json 里少了 "component": true,四个文件不会被识别为一个组件。 修法:补上该配置;同时确认 .js 里用的是 Component({}) 而不是 Page({})——用 Page 注册时 properties 收不到值、attached 也不会按预期执行。

4. 在页面里改组件内部样式,怎么都不生效 现象:审查元素看到类名了,页面 wxss 里写了 background-color 却没反应。 原因:组件样式默认隔离,外部样式进不去;即使开了 addGlobalClass,还可能因为权重不够被组件内部样式覆盖。 修法:组件 options 里加 addGlobalClass: true,页面侧必要时加 !important 或提高选择器权重;更推荐用 externalClasses 从外部传类名进去。

5. 组件内写了标签选择器,把整个页面搞乱 现象:组件里写了 view { ... },结果页面里所有 view 都变了样。 原因:组件内使用标签选择器会作用到页面上的同类标签,即使开了样式隔离也一样。 修法:组件内一律用类选择器,禁用标签、ID、属性选择器。

6. 多插槽内容全塞进第一个位置 现象:写了两个 <slot name="title"> 和 <slot name="right">,填充的内容却都跑到第一处。 原因:没有开启多插槽。 修法:组件 options 里加 multipleSlots: true,使用侧用 slot="title" 显式指定。

7. 在 created 里调用 setData 报错 现象:组件一创建就报 setData 相关错误,数据没渲染。 原因:created 阶段组件实例还没初始化完,不能 setData,也不能操作节点。 修法:数据初始化、请求、定时器统一放到 attached;created 只做纯 JS 层面的赋值。

8. 布尔属性传 back="false" 结果组件当成 true 现象:<navigation-bar back="false"> 里的返回箭头还是显示出来了。 原因:属性值没加 &#123;&#123;&#125;&#125; 时是字符串 "false",转布尔恒为真。 修法:写 back="&#123;&#123;false&#125;&#125;";数字同理写 delta="&#123;&#123;2&#125;&#125;"。

9. 生命周期写在页面里不触发 现象:onLaunch、onShow 写在某个页面的 js 里,启动时完全没打印。 原因:应用生命周期的注册入口是 App({}),位置固定在 app.js,页面里写不生效。 修法:挪到 app.js 的 App() 中;页面级逻辑仍写在 Page() 里。

10. Vant 组件引入后样式错乱或路径找不到 现象:usingComponents 写了 @vant/weapp/button/index,编译提示找不到组件。 原因:npm 包没构建,或者全局 "style": "v2" 还在,导致样式异常。 修法:先“构建 npm”生成 miniprogram_npm,删掉 app.json 里的 "style": "v2",路径写 @vant/weapp/button/index(不带 ./ 或 /)。

练习 ​

练习 1|按模块拆分主包 导入第三天的 MP demo(当前 3.3M,无法上传),按业务模块拆出至少两个分包(如 packages_house、packages_repair),把对应二级页面和大图资源分别移入各自分包。要求:主包体积降到 1.5M 以下,从首页能正常 navigateTo 到两个分包的列表页,并截图“详情 → 代码依赖分析”前后对比。

练习 2|分包预加载配置 在练习 1 的基础上,为首页 pages/index/index 配置 preloadRule,network 设为 all,同时预加载两个分包。打开首页后在控制台确认预加载成功日志;再把 network 改回默认(仅 wifi),切换到“3G”网络模拟环境,对比两次控制台输出的差异并说明原因。

练习 3|封装 navigation-bar 组件 封装一个导航栏组件并全局注册,要求:支持 back(布尔,默认 false)、delta(数字,默认 1)两个属性;提供名为 title 的具名插槽;点击返回区域时调用 wx.navigateBack({ delta })。在页面 json 中关闭默认导航栏后使用它,并在两个不同页面中复用,验证全局注册与页面局部注册的差别。

练习 4|用 externalClasses 定制倒计时按钮 封装一个倒计时按钮组件:attached 时开始每秒递减,剩余秒数通过 triggerEvent('change', time) 抛给页面;通过 externalClasses 暴露 custom-class,让页面从外部改变按钮背景色和圆角。要求页面中同时放两个该组件,传入不同的外部类名,验证样式互不影响。

练习 5|应用更新提示 在 app.js 的 onLaunch 中接入 wx.getUpdateManager():检测到新版本 → 下载完成 → wx.showModal 提示“是否重启应用新版本”,用户点确定则 applyUpdate()。用开发者工具“编译设置 → 下一次编译时模拟更新”验证全流程,并额外打印 options.scene 与 options.query,确认扫码进入时的场景值和启动参数。

原视频对照 ​

#视频时长
1反馈-复习-学习目标19:59
2生命周期-应用生命周期13:46
3生命周期-更新应用提示09:39
4分包加载-分包配置16:45
5分包加载-分包预下载08:25
6自定义组件-基本用法13:44
7自定义组件-自定义导航栏08:11
8自定义组件-修改组件样式13:10

小结 ​

  1. 分包拆不拆,看的是“首屏要不要”,不是“放不放得下”。 2M 是硬上限(超了连预览都发不出去),1.5M 才是体验红线,先拆大资源再拆页面。
  2. tabBar 是分包的唯一硬禁区,其余二级页面按业务模块整块搬走即可,跳转路径只是多了 root 前缀,写法没有本质变化。
  3. 预加载是拿流量换速度的赌注,只在用户大概率会去的入口页配,配得越宽,首屏被拖慢的风险越大。
  4. 组件默认样式隔离是保护而非障碍。 改样式的优先级应当是:externalClasses 传类名 > addGlobalClass + 权重,前者可控、后者容易污染。
  5. 组件内禁止标签选择器,这条比“记得开样式隔离”更容易踩,也更容易造成全局污染。
  6. 组件通信只有两条主干道:properties 下行、triggerEvent 上行。数据一复杂就该考虑拆组件而不是加属性。
  7. 生命周期选时机的判断标准很简单:需要 setData 或拿实例的,一律放 attached;created 只做纯逻辑。
  8. 组件库是别人的组件,引入前先确认“构建 npm + 移除 style: v2”这两步,再评估样式覆盖成本,别等到页面写完了才发现改不动。