用AI做小程序,先看懂这12个前端名词(附避坑指南)
文章目录
最近在做自己的社群小程序AfterNoise,做到前端时,遇到了一批不熟悉的名字:WXML、WXSS、Page、Component、setData。看着AI生成的文件和代码,我也得先弄明白,这些东西分别是干什么的。
网页里常见的是HTML、CSS和JavaScript。HTML全称是HyperText Markup Language,中文是“超文本标记语言”,用来描述网页里的标题、段落、图片等结构;微信原生小程序里又出现了.wxml、.wxss和页面配置文件。WXML跟HTML有什么关系?点击按钮以后,究竟是哪份代码在处理?这些名词得放进具体页面里,才容易看出各自的作用。
这篇就模拟做一个咖啡馆点单小程序。打开菜单,选择拿铁,再选大杯、冰,加入购物袋,改成两杯,最后确认订单。接下来出现的文件、字段和按钮,都围绕这一套流程解释。
先记住四个分工:WXML写结构,WXSS写样式,JavaScript或TypeScript写逻辑,JSON写配置。 本篇使用微信原生JavaScript小程序;使用跨端框架时,源文件名称和更新数据的写法可能不同。
先看看,我们准备做哪几个页面
虚构门店叫“Punk咖啡馆”,把点单分成四个页面,能看清各个部分怎样接起来。下面用示意图演示这套点单界面;确认页使用“模拟下单”,完成后显示“演示完成”,全程不扣款。
-
菜单:看有哪些咖啡,从商品行进入规格页。
-
饮品规格:选杯型和冷热,确认单价,加入购物袋。
-
购物袋:核对规格,修改数量,计算小计和总价。
-
确认订单:查看自取门店和完整清单,观察提交过程与结果。
01. WXML:菜单、文字和按钮怎么组成页面
WXML的全称是 WeiXin Markup Language,是微信小程序描述界面结构的标记语言。打开咖啡菜单,你看到的商品行里有杯子图标、名称、介绍、价格和“选规格”按钮,这些部件的位置与包含关系由WXML声明。
view是容器组件,负责把一组内容放在一起;text是文字组件,button是按钮组件。标签里的class叫“样式类”,相当于给部件贴一个名称,WXSS按这个名称找到它、设置外观。比如本例项目的商品组件里,外层view包住一整行,内部还有一个view把名称、介绍与价格放在一起;下面保留了它的实际结构。
<view class="drink-card">
<text class="cup">☕</text>
<view class="info">
<text class="name">{{drink.name}}</text>
<text class="description">{{drink.description}}</text>
<text class="price">¥{{drink.priceText}}</text>
</view>
<button class="choose" bindtap="onChoose">选规格</button>
</view>
图里的外框对应view,名称和价格对应text,“选规格”对应button。杯子在本例中使用文字图标;需要放真实饮品照片时,再使用image组件,并提供图片地址。
遇到商品名没显示、价格被放错区域,先看WXML里的标签和字段。网页的div、span等标签不能直接照搬进微信原生小程序,要使用对应的小程序组件。
02. WXSS:规格选项为什么有不同颜色
WXSS的全称是 WeiXin Style Sheets,负责界面的颜色、尺寸、间距与布局。规格页里,中杯和大杯横向排在一起,已选项有浅绿底和绿色边框,未选项是白底,这些视觉规则都写在WXSS里。
你之前提到的名称准确写作 CSS:Cascading Style Sheets,层叠样式表。微信原生小程序使用.wxss样式文件,已有CSS知识能帮助理解它,具体属性和选择器仍要看小程序环境的支持情况。
.options {
display: flex;
gap: 8px;
margin-bottom: 20px;
}
.option {
flex: 1;
min-width: 0;
background: white;
border: 1px solid #d9decf;
}
.selected {
background: #edf0e5;
color: #43532b;
border-color: #596b38;
}
这里的 Flex叫“弹性布局”,用来安排一组部件的排列和伸缩;display: flex让这个容器采用弹性布局,示例中的两个选项默认横向排。flex: 1让它们分配可用空间,min-width: 0允许部件收缩,避免内容把布局撑宽。
样式里还会看到这些英文,它们都能对应到图上的具体位置:
-
gap:部件之间的间距,gap: 8px表示两个选项之间留8px。
-
padding:内边距,指面板边缘到内部内容的距离,本例为16px。
-
margin-bottom:下外边距,指这组选项与下方内容之间的距离,本例为20px。
-
background:背景,white表示白色。
-
border:边框,1px solid表示1px粗的实线;border-color设置边框颜色。
-
color:文字颜色,在这里决定选项里的字是什么颜色。
.options、.option和.selected都是本例自己起的样式类名,分别表示“选项组”“单个选项”“已选状态”。它们可以改名,但WXML和WXSS里要一起改;具体哪个选项获得selected,由当前数据决定。
你给的星巴克截图里,大杯也用浅绿底和绿色文字表达选中状态。对照自己的规格页,可以检查已选与未选是否容易分辨,价格是否与当前选项一致。
颜色、间距、圆角不对,先检查WXSS和WXML里的class。选中颜色变了、价格没变,则要继续查后面的逻辑与数据更新。
03. JavaScript / TypeScript:选大杯以后,价格怎么算
JavaScript通常简称 JS,用于写交互和业务逻辑。用户选“大杯”,拿铁单价从24元变成28元;加入购物袋后,同规格要合并数量;点击确认以后,还要处理提交中与完成状态,这些都需要逻辑代码。
TypeScript简称 TS,在JavaScript基础上增加类型系统,能在开发时检查一部分错误。比如饮品名称是字符串,价格是数字,规格只接受限定值;TS需要经过项目工具链处理,类型检查也不能代替实际业务校验。本例使用JS。
计价时,示例把24元保存为2400分,大杯加400分。界面显示时再换算成元,数量改为2以后,用2800 × 2得到5600分,也就是56元。
如果把拿铁大杯冰和中杯热放进购物袋,要分成两条记录;两杯完全相同的拿铁大杯冰才合并。商品ID、杯型、冷热共同决定一条购物袋记录,只看商品名会把不同规格算到一起。这里的 ID是Identifier的缩写,意思是“标识符”;latte是这份菜单给拿铁起的ID,代码用它准确找到这一款饮品。
04. JSON:小程序知道有哪些页面
JSON的全称是 JavaScript Object Notation,是一种数据格式。小程序用JSON文件保存配置,例如有哪些页面、导航栏标题是什么,以及页面引用哪些自定义组件。
这次的app.json声明四个页面,第一项菜单页就是默认启动页。下面这段可以直接对应项目里的目录,路径不带.wxml或.js扩展名。pages表示“页面列表”,window表示“窗口配置”,navigationBarTitleText表示“导航栏标题文字”;这些是微信规定的配置字段,拼写要准确。
{
"pages": [
"pages/menu/menu",
"pages/drink/drink",
"pages/cart/cart",
"pages/order/order"
],
"window": {
"navigationBarTitleText": "Punk咖啡馆"
}
}
每个页面还可以有自己的JSON。例如drink.json设置"navigationBarTitleText": "饮品规格",进入这一页,顶部显示的标题就变了。JSON使用双引号,标准JSON不接受注释和末尾多余的逗号,格式错误会影响编译。
找不到页面,先核对配置路径与文件目录;顶部标题不对,检查全局和页面配置。商品行里的圆角和价格字号,继续到WXSS里调整。
05. Page:菜单和规格是两个独立页面
Page的英文意思是“页面”,Page(...)是微信原生小程序注册页面的接口:把这一页的数据和处理函数交给微信,让系统按页面规则运行。本例把菜单、规格、购物袋、确认订单安排成四个页面,每页都有自己的路径、数据和事件处理;进入规格页时,还需要知道用户选了哪款咖啡。
点击拿铁的“选规格”,页面打开/pages/drink/drink?id=latte。这里的id=latte是传给规格页的参数,规格页在onLoad里接收它,再从菜单数据中找到对应饮品。参数就是进入页面时一并带过去的信息,所以规格页知道这次打开的是拿铁。
onLoad(options) { // 页面加载时执行,options里放着进入页面的参数
const drink = shop.findDrink(options.id) // 用饮品ID找到拿铁
if (!drink) {
wx.showToast({ title: '饮品不存在', icon: 'none' })
return
}
this.setData({
drink,
priceText: shop.money(drink.baseCents)
})
}
onLoad、onShow:到底什么时候执行?
这些名称前面的on表示“当某件事发生时”,后面的Load是“加载”,Show是“显示”,Hide是“隐藏”,Unload是“卸载”。它们是微信规定的页面函数名,注意大小写:写onLoad,不能随意改成onload。
页面从创建、显示到隐藏、销毁,会经历一组过程,叫生命周期。在这些时机由系统自动调用的函数,叫生命周期回调;“回调”指你先写好函数,等对应事情发生,系统再来执行它。
-
onLoad:页面加载时执行。 每创建一个新的页面实例,执行一次。比如进入拿铁规格页,用它接收id=latte、准备饮品数据;“实例”指这次打开后创建出来的具体页面对象。 -
onShow:页面显示时执行。 第一次出现会执行,从其他页面返回、重新露出来也会执行。菜单页在这里重新读取购物袋,让杯数和总价跟上最新数据。 -
onReady:页面第一次渲染完成时执行。 此时初始界面已经完成渲染,适合安排依赖界面已经准备好的操作,比如测量部件尺寸;同一个页面实例执行一次。 -
onHide:页面被隐藏时执行。 比如菜单打开规格页,菜单暂时退到后面,仍然保留;可按业务需要暂停当前页面的计时或播放。 -
onUnload:页面被卸载时执行。 比如从规格页点左上角返回,这个规格页被移出页面栈,可在这里清理它的定时器;“页面栈”就是系统按进入顺序保存的一组页面。
拿菜单和规格页走一遍,就能看出“加载”与“显示”的区别。下面显示的是生命周期触发顺序:进入规格页后,使用左上角返回按钮回到原菜单。没有定义某个回调函数时,就不会执行那一项自定义处理。
首次打开菜单
→ 菜单 onLoad(加载)
→ 菜单 onShow(显示)
→ 菜单 onReady(首次渲染完成)
点击拿铁,进入规格页
→ 菜单 onHide(暂时隐藏)
→ 规格页 onLoad / onShow / onReady
点左上角返回菜单
→ 规格页 onUnload(卸载)
→ 原菜单 onShow(再次显示)
返回仍保留的菜单时,菜单会再次执行onShow,不会重新执行onLoad。 如果用wx.reLaunch关闭原来的页面再新建菜单,就会创建新实例,重新经历加载;本例的“继续选咖啡”“返回菜单”按钮使用了这种方式。函数只在需要时定义即可,不必为了凑齐生命周期,把五个函数全部写进项目。
上面代码中的options是页面接收到的参数对象,options.id取出饮品ID;const声明一个本次不再重新赋值的变量,return结束当前函数的执行,不会让页面自动返回。shop.findDrink则是本例自己写的“查找饮品”函数,并非微信API。设计页面时,把进入参数、返回位置和刷新时机一起安排。规格页返回菜单后还能继续选择,完成演示订单后再回菜单,购物袋则应当清空。
06. Component:三款咖啡共用一个商品组件
Component就是组件。除了微信提供的view、text等基础组件,项目还能把一组结构、样式与行为封装成自定义组件;这里的drink-card就是一个商品行组件。
美式、拿铁、摩卡都用相同排版,传入的名称、说明和价格各不相同。菜单页通过usingComponents(声明本页要使用哪些自定义组件)引用它,再把每款饮品的数据传进去;点击组件内的按钮,组件发出choose事件,把饮品ID交给菜单页。
<drink-card
wx:for="{{drinks}}"
wx:key="id"
drink="{{item}}"
bind:choose="onChoose"
/>
这里的drink是组件接收的属性,意思是“饮品”;drinks是饮品列表,item是循环中当前这一款饮品。wx:key="id"用每款饮品的ID标识列表项,帮助系统识别是哪一项;bind:choose监听这个组件发出的“选择饮品”事件。
组件自己的JSON声明"component": true,这里的true表示“是/开启”;false表示“否/关闭”。组件逻辑用Component(...)注册,菜单页面用Page(...)注册,它们各有职责。
想把“选规格”按钮统一加宽,就修改这个组件,三款饮品一起变化。复用时也要检查长名称、缺少介绍和价格位数,避免组件只适合一条短数据。
07. 数据绑定:同一套界面显示不同咖啡
数据绑定把数据与界面字段连接起来,WXML里常见的写法是双花括号{{ }}。规格页的{{drink.name}}显示当前饮品名,{{priceText}}显示当前价格,购物袋的{{item.quantity}}显示这一行的杯数。
<text class="title">{{drink.name}}</text>
<text class="price">¥{{priceText}}</text>
菜单用wx:for遍历三款咖啡,反复使用同一个商品组件;购物袋用wx:if判断有没有商品,没商品时显示“购物袋还没有咖啡”。这样每个状态都有对应的界面,用户能知道下一步去哪里。for表示循环,if表示条件判断;wx:for负责按列表逐项显示,wx:if负责决定一块内容是否出现。
模板负责显示数据。大杯加多少钱、两杯合计多少,先由JS算出结果,再放进模板;复杂计算不要塞进双花括号里。
08. 事件绑定:点击“大杯”,接到哪个函数
事件是用户或组件发出的动作通知,例如点击、输入和滚动。事件绑定把通知接到处理函数,bindtap或bind:tap用于绑定点击;按钮上写函数名称,不加()。
规格页的两个杯型按钮都调用onSize,按钮上的data-size告诉函数点击的是哪个。中杯传medium,大杯传large,JS从event.currentTarget.dataset.size里取出这个值。medium和large是“中号”“大号”的英文值,本例用它们区分杯型;它们也可以换成其他约定,但按钮和逻辑必须使用同一套值。冷热选项同理,hot表示热,iced表示冰。
<button data-size="large" bindtap="onSize">
大杯 +¥4
</button>
这行代码里,bind是“绑定”,tap是“轻点”,所以bindtap表示“把点击接到这个函数”。event是本次事件的信息,currentTarget是绑定该处理函数的组件,dataset存放该组件的data-*自定义数据;dataset.size就读取了按钮上的data-size。
onSize是本例自己起的函数名,表示“处理杯型选择”;onChoose表示“处理饮品选择”,onAdd表示“加入购物袋”,onSubmit表示“提交订单”。这些自定义函数需要通过事件绑定等方式调用;onLoad和onShow则由微信按生命周期调用。
事件接上以后,函数才能计算价格并更新选中项。若整条商品行与内部按钮都有点击行为,还要检查事件冒泡:内部点击可能继续传到外层;需要阻止向外传递时,可以使用catchtap。本例只给“选规格”按钮接点击,商品行本身不跳转。
09. setData:价格和选中颜色一起更新
set是“设置”,data是“数据”,setData就是“设置页面数据”的接口。在常见的微信原生Page写法里,它用于更新页面数据,并把变化交给视图层。拿铁从中杯改成大杯时,除了金额要变,选中的杯型也要变;在一次更新中提交这两个字段,界面就能表达同一个选择。
下面是onSize中的实际更新,line.unitText已经由计价函数算好。this在这个页面处理函数里指当前页面对象,this.setData(...)就是调用当前页面的数据更新接口;size是杯型,priceText是准备显示的价格文字。点击大杯以后,size变成large,价格文字变成28。
this.setData({
size,
priceText: line.unitText
})
WXML把当前值接到样式:class="option {{size === 'large' ? 'selected' : ''}}"。数据绑定先声明哪里要读取数据,setData再更新数据;只直接修改this.data,不能可靠地让页面一起刷新。
订单提交时也用同样的连接方式,更新submitting以后,按钮出现加载图标并禁用。submitting表示“正在提交”,loading表示“正在加载”,disabled表示“禁用”,done表示“已完成”;本例把submitting同时绑定到按钮的loading与disabled属性。视图更新是异步过程,逻辑层还要先设置同步标记拦住重复点击,不能仅依赖按钮稍后变灰。
10. wx API:跳转、提示和本地保存
API的全称是 Application Programming Interface,应用程序编程接口。微信客户端提供的接口通常以wx.开头,这个咖啡馆示例用它来打开页面、弹出提示和保存购物袋。
菜单到规格页,用wx.navigateTo;添加以后,用wx.showToast提示“已加入购物袋”。购物袋用wx.setStorageSync保存、wx.getStorageSync读取,所以返回菜单或者重新打开小程序,仍可以读到本机保留的商品。这几个名字可以拆开读:
-
navigateTo:导航到一个页面。 打开规格页,同时保留原菜单,之后能返回。 -
showToast:显示短提示。 Toast在界面设计里指短暂出现的提示,例如“已加入购物袋”。 -
setStorageSync:同步写入本地存储。 Storage是存储,set是写入,Sync是同步,当前操作结束后才继续下一行。 -
getStorageSync:同步读取本地存储。 get是读取,用它拿回已经保存的购物袋。 -
reLaunch:重新打开页面。 关闭当前页面栈里的所有页面,打开目标页面;本例用它回到一个新的菜单页。
wx.navigateTo({ url: '/pages/drink/drink?id=latte' })
wx.showToast({ title: '已加入购物袋', icon: 'success' })
真实咖啡馆还可能需要门店定位、请求菜单和微信支付,它们分别有对应的接口与接入条件。本例固定演示门店,只做本地模拟;真实价格、库存和支付结果,需要由业务服务校验,本地存储不能确认它们。
11. rpx / vw:同一份菜单放进不同屏宽
rpx是WXSS支持的相对长度单位,页面总宽度按750rpx计算。页面宽375px时,32rpx等于16px;页面宽430px时,32rpx约等于18.35px。这里的px是布局中的逻辑像素,与屏幕硬件的物理像素数量不同。
px是pixel(像素)的缩写;vw是viewport width(视口宽度)的缩写,“视口”指当前用于显示页面的区域。vw是CSS标准里的视口宽度单位,1vw等于视口宽度的1%。本例的页面左右留白用4vw:375px下是15px,430px下是17.2px;微信当前WXSS文档优先推荐vw,也提醒不要滥用按屏宽计算的单位。
商品行依靠Flex让中间信息区域伸缩,按钮保留可点击的尺寸;内容区设置620px最大宽度,宽屏上不会无限拉长。换算单位以后,还要检查长名称、按钮宽度与底栏遮挡,把所有px直接换成rpx,不能自动解决排版问题。
12. WebView / Skyline:最后由谁把界面画出来
渲染器负责把结构和样式呈现成屏幕上的界面。微信小程序常见的渲染方案有WebView和Skyline。WebView是基于网页引擎的渲染方案,Skyline是微信提供的另一套小程序渲染引擎名称;它们的实现与支持范围不同;选择方案以后,要核对组件、样式、基础库和设备等条件。
这里的WebView指渲染方案,小程序的web-view则是承载网页的组件,名称相近、用途不同。咖啡馆示例使用默认WebView方案,没有配置Skyline;也没有把HTML文件嵌进去代替点单页面。
比如规格页用了Flex、gap和按钮样式,底栏还用了安全区留白,都要在目标运行环境里检查。浏览器中的UI示意与代码逻辑验证,不能确认微信里最终的字形、间距、滚动和系统默认样式;文章后面的避坑部分,会接着说我在AfterNoise遇到的差异。
我遇到的一个坑:HTML预览正常,小程序里却变了
做AfterNoise时,我遇到了一个坑。Codex先帮我做好UI,给我一个HTML文件预览,在浏览器里看着没问题。后来我把小程序代码推送到GitHub,再打开小程序页面预览,才发现排版和原来看到的效果有差异。
其中一个问题就出在底部导航:原来设计了五个入口,到了小程序里,后面的入口被挤出了屏幕。HTML和微信原生小程序使用的组件、默认样式和运行环境不同,浏览器里确认过的宽度、间距与滚动效果,还得在小程序里重新检查。
所以,做小程序一定要用微信开发者工具确认预览。 GitHub保存的是项目源码;推送完成以后,还要把最新的小程序项目导入开发者工具,编译并在模拟器里查看。准备给别人使用时,再生成预览二维码,用手机微信检查一次。
检查时先看三个地方:底部入口是不是都在,页面滚动到底有没有被按钮遮住,主要按钮点下去有没有正确反馈。发现差异,就回到实际的小程序文件里修改,重新编译;每次比较时,确认打开的是同一版代码。
小程序预览的操作流程
接着用前面的Punk咖啡馆演示如何打开预览。操作截图来自微信开发者工具稳定版2.02.2608070,红框标出要点击的位置,隐私信息已打码。
第一步:打开微信开发者工具
从官方下载页下载安装稳定版。Mac选择对应的Apple芯片或Intel版本,Windows选择对应的安装包;安装后打开工具,用手机微信扫码登录。进入项目列表后,点击右上角的导入。
第二步:选择小程序项目文件夹
这里选择的是已经制作好的小程序源码文件夹。本次演示的文件夹叫“示例代码”;操作自己的项目时,选择自己的代码目录。截图里第一层能看到app.json和project.config.json,它们能帮助确认选对了项目根目录。“根目录”就是项目最外层的文件夹。
只有HTML文件时,需要先让Codex生成原生小程序项目文件。 HTML设计预览不能直接作为这份原生项目导入;这里需要的是WXML、WXSS、JS与JSON等文件。
第三步:填写导入信息
AppID是小程序的应用标识。已有小程序,就填写后台提供的AppID,并使用有开发权限的微信账号登录;没有账号时,可以点击表单里的“测试号”,本次演示使用了这个入口。
-
项目名称:本例填写punk-cafe-demo,用于在本地识别项目。
-
目录:刚才选中的小程序源码文件夹。
-
AppID:自己的小程序或测试号AppID。
-
后端服务:本例选“不使用云服务”,点单数据保存在本机。
-
开发模式:选择“小程序”。
核对后点击右下角的创建,打开已有项目。不同版本可能显示“导入”或“打开”,按已有项目的入口操作。
第四步:点击编译,查看模拟器
顶部编译条件选择普通编译,再点编译入口。本次版本用圆形箭头表示编译,旁边能看到“普通编译”;也可以使用菜单“工具 → 编译”。编译完成后,模拟器显示Punk咖啡馆菜单。
编译就是让工具处理代码并运行项目;模拟器就是电脑上显示小程序界面的区域。如果编译报错,在调试器的Console(控制台)里查看第一条错误,再回到相应文件修改。
第五步:对照点单例子,检查页面
前面演示的是:菜单选拿铁 → 大杯、冰 → 加入购物袋 → 数量改成两杯 → 确认订单。这里对照同一条流程,检查选中样式是否清楚、单价是否为28元、两杯是否为56元。
再切换模拟器机型,看底部按钮是否遮住内容,商品名称是否被挤出屏幕。示例中的“模拟下单”只演示提交状态,不产生真实付款。
第六步:点击预览,生成手机二维码
保存文件,确认编译通过,然后点工具栏的预览,选择二维码入口。等待工具生成二维码,再用具有该AppID预览权限的微信账号扫描;测试号通常使用登录工具和申请测试号的同一个微信账号。
截图中的二维码已打码,操作时扫描自己工具刚生成的二维码。在手机里对照菜单、规格和购物袋,检查排版、滚动与点击反馈。
每次修改后,都按“保存 → 编译 → 预览 → 扫码”重新确认。 推送GitHub只更新源码,手机原来打开的页面可能仍是旧版本。比较HTML设计和小程序效果时,确认它们对应同一版页面、同一组选项和同一份内容。
关于作者
Punk|中科大管理学硕士|AI提示词、AI小白教程|Punk系列Skills作者|3个月赚了8位数|Learn in Public|FDE文章浏览量240w|@AdrianPunk115
























