慣性聚合 高效追蹤和閱讀你感興趣的部落格、新聞、科技資訊
閱讀原文 在慣性聚合中打開

推薦訂閱源

博客园 - 司徒正美
V
V2EX
T
Tailwind CSS Blog
有赞技术团队
有赞技术团队
aimingoo的专栏
aimingoo的专栏
Apple Machine Learning Research
Apple Machine Learning Research
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
A
About on SuperTechFans
月光博客
月光博客
T
The Blog of Author Tim Ferriss
宝玉的分享
宝玉的分享
Martin Fowler
Martin Fowler
博客园 - 聂微东
The GitHub Blog
The GitHub Blog
V
Visual Studio Blog
WordPress大学
WordPress大学
酷 壳 – CoolShell
酷 壳 – CoolShell
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI

阮一峰的网络日志

科技爱好者周刊(第 396 期):互联网通信的替代方案 科技爱好者周刊(第 396 期):互联网通信的替代方案 - 阮一峰的网络日志 科技爱好者周刊(第 395 期):软件开发的第三种方式 科技爱好者周刊(第 395 期):软件开发的第三种方式 - 阮一峰的网络日志 科技爱好者周刊(第 393 期):脑腐状态 科技爱好者周刊(第 392 期):axios 投毒与好莱坞式骗术 科技爱好者周刊(第 391 期):AI 的贫富分化 科技爱好者周刊(第 390 期):没有语料,大模型就是智障 套壳中国大模型撑起500亿美元估值?扒一扒 Cursor 的"套壳"疑云 科技爱好者周刊(第 389 期):未来如何招聘程序员 科技爱好者周刊(第 388 期):测试是新的护城河 零安装的"云养虾":ArkClaw 使用指南 科技爱好者周刊(第 387 期):你是领先的 科技爱好者周刊(第 386 期):当外卖员接入 AI 字节全家桶 Seed 2.0 + TRAE 玩转 Skill 科技爱好者周刊(第 385 期):马斯克害怕中国车企吗? 智谱旗舰 GLM-5 实测:对比 Opus 4.6 和 GPT-5.3-Codex 科技爱好者周刊(第 384 期):为什么软件股下跌 科技爱好者周刊(第 383 期):你是第几级 AI 编程 Kimi 的一体化,Manus 的分层 科技爱好者周刊(第 382 期):独立软件的黄昏 AI native Workspace 也许是智能体的下一阶段 科技爱好者周刊(第 381 期):中国 AI 大模型领导者在想什么 科技爱好者周刊(第 380 期):为什么人们拥抱"不对称收益" 科技爱好者周刊(第 379 期):《硅谷钢铁侠》摘录 我如何用 AI 处理历史遗留代码:MiniMax M2.1 升级体验 科技爱好者周刊(第 378 期):预测是新的互联网热点 科技爱好者周刊(第 377 期):14万美元的贫困线 科技爱好者周刊(第 376 期):太空数据中心的争议 科技爱好者周刊(第 375 期):一扇门的 Bug 终于有人做了 Subagent,TRAE 国内版 SOLO 模式来了 科技爱好者周刊(第 374 期):6GHz 的问题 VS Code 使用国产大模型 MiniMax M2 教程 科技爱好者周刊(第 373 期):数据模型是新产品的核心 国产大模型接入 Claude Code 教程:以 Doubao-Seed-Code 为例 科技爱好者周刊(第 372 期):软件界面如何设计 大模型比拼:MiniMax M2 vs GLM 4.6 vs Claude Sonnet 4.5 科技爱好者周刊(第 371 期):一个乐观主义者的专访 科技爱好者周刊(第 370 期):正确的代码高亮 错误处理:异常好于状态码 科技爱好者周刊(第 369 期):Tim 与罗永浩的对谈 科技爱好者周刊(第 368 期):不要这样管理软件团队 一天之内,智谱和 Anthropic 都发了最强编程模型 科技爱好者周刊(第 367 期):Nano Banana 的几个妙用 科技爱好者周刊(第 366 期):旧金山疯狂的 AI 广告 科技爱好者周刊(第 365 期):流量变现正在崩塌 科技爱好者周刊(第 364 期):最难还原的魔方 科技爱好者周刊(第 363 期):最好懂的神经网络解释 科技爱好者周刊(第 362 期):GitHub 工程师谈系统设计 科技爱好者周刊(第 361 期):暗网 Tor 安全吗?
微信小程序入門教程之三:腳本編程
阮一峰 · 2020-10-29 · via 阮一峰的网络日志

這個系列教程的前兩篇,介紹了小程序的項目結構頁面樣式

今天,接著往下講,教大家為小程序加入 JavaScript 腳本,做出動態效果,以及如何跟用戶互動。學會了腳本,就能做出複雜的頁面了。

本篇的難度要大於前兩篇,如果覺得不好理解,可以先跟著例子,動手做一遍,然後再讀文字說明,可能就容易理解了。

所有示例的完整代碼,都可以從 GitHub 的代碼倉庫下載。

一、數據綁定

前面的所有示例,小程序的頁面都是寫死的,也就是頁面內容不會變。但是,頁面數據其實可以通過腳本傳入,通過腳本改變頁面,實現動態效果。

小程序提供了一種特別的方法,讓頁面可以更方便地使用腳本數據,叫做"數據綁定"(data binding)。

所謂"數據綁定",指的是腳本里面的某些數據,會自動成為頁面可以讀取的全局變量,兩者會同步變動。也就是說,腳本里面修改這個變量的值,頁面會隨之變化;反過來,頁面上修改了這段內容,對應的腳本變量也會隨之變化。這也叫做 MVVM 模式。

下面看一個例子。打開home.js文件,改成下面這樣。


Page({
  data: {
    name: '張三'
  }
});

上面代碼中,Page()方法的配置對象有一個data屬性。這個屬性的值也是一個對象,有一個name屬性。數據綁定機制規定,data對象的所有屬性,自動成為當前頁面可以讀取的全局變量。也就是說,home頁面可以自動讀取name變量。

接著,修改home.wxml文件,加入name變量。


<view>
  <text class="title">hello {{name}}</text>
</view>

上面代碼中,name變量寫在{{...}}裡面。這是小程序特有的語法,兩重大括號表示,內部不是文本,而是 JavaScript 代碼,它的執行結果會寫入頁面。因此,{{name}}表示讀取全局變量name的值,將這個值寫入網頁。

注意,變量名區分大小寫,nameName是兩個不同的變量名。

開發者工具導入項目代碼,頁面渲染結果如下。

可以看到,name變量已經自動替換成了變量值"張三"。

這個示例的完整代碼,可以查看代碼倉庫

頁面和腳本對於變量name是數據綁定關係,無論哪一方改變了name的值,另一方也會自動跟著改變。後面講解到事件時,會有雙方聯動的例子。

二、全局數據

數據綁定只對當前頁面有效,如果某些數據要在多個頁面共享,就需要寫到全局配置對象裡面。

打開app.js,改寫如下。


App({
  globalData: {
    now: (new Date()).toLocaleString()
  }
});

上面代碼中,App()方法的參數配置對象有一個globalData屬性,這個屬性就是我們要在多個頁面之間分享的值。事實上,配置對象的任何一個屬性都可以共享,這裡起名為globalData只是為了便於識別。

然後,打開home.js,改成下面的內容,在頁面腳本里面獲取全局對象。


const app = getApp();

Page({
  data: {
    now: app.globalData.now
  }
});

上面代碼中,getApp()函數是小程序原生提供的函數方法,用於從頁面獲取 App 實例對象。拿到實例對象以後,就能從它上面拿到全局配置對象的globalData屬性,從而把app.globalData.now賦值給頁面腳本的now屬性,進而通過數據綁定機制,變成頁面的全局變量now

最後,修改一下頁面代碼home.wxml


<view>
  <text class="title">現在是 {{now}}</text>
</view>

開發者工具導入項目代碼,頁面渲染結果如下。

可以看到,頁面讀到了全局配置對象app.js裡面的數據。

這個示例的完整代碼,可以查看代碼倉庫

三、事件

事件是小程序跟用戶互動的主要手段。小程序通過接收各種用戶事件,執行回調函數,做出反應。

小程序的常見事件有下面這些。

  • tap:觸摸後馬上離開。
  • longpress:觸摸後,超過 350ms 再離開。如果指定了該事件的回調函數並觸發了該事件,tap事件將不被觸發。
  • touchstart:觸摸開始。
  • touchmove:觸摸後移動。
  • touchcancel:觸摸動作被打斷,如來電提醒,彈窗等。
  • touchend:觸摸結束。

上面這些事件,在傳播上分成兩個階段:先是捕獲階段(由上層元素向下層元素傳播),然後是冒泡階段(由下層元素向上層元素傳播)。所以,同一個事件在同一個元素上面其實會觸發兩次:捕獲階段一次,冒泡階段一次。詳細的介紹,請參考我寫的事件模型解釋

小程序允許頁面元素,通過屬性指定各種事件的回調函數,並且還能夠指定是哪個階段觸發回調函數。具體方法是為事件屬性名加上不同的前綴。小程序提供四種前綴。

  • capture-bind:捕獲階段觸發。
  • capture-catch:捕獲階段觸發,並中斷事件,不再向下傳播,即中斷捕獲階段,並取消隨後的冒泡階段。
  • bind:冒泡階段觸發。
  • catch:冒泡階段觸發,並取消事件進一步向上冒泡。

下面通過一個例子,來看如何為事件指定回調函數。打開home.wxml文件,改成下面的代碼。


<view>
  <text class="title">hello {{name}}</text>
  <button bind:tap="buttonHandler">點擊</button>
</view>

上面代碼中,我們為頁面加上了一個按鈕,併為這個按鈕指定了觸摸事件(tap)的回調函數buttonHandlerbind:前綴表示這個回調函數會在冒泡階段觸發(前綴裡面的冒號可以省略,即寫成bindtap也可以)。

回調函數必須在頁面腳本中定義。打開home.js文件,改成下面的代碼。


Page({
  data: {
    name: '張三'
  },
  buttonHandler(event) {
    this.setData({
      name: '李四'
    });
  }
});

上面代碼中,Page()方法的參數配置對象裡面,定義了buttonHandler(),這就是<button>元素的回調函數。它有幾個地方需要注意。

(1)事件回調函數的參數是事件對象event,可以從它上面獲取事件信息,比如事件類型、發生時間、發生節點、當前節點等等。

(2)事件回調函數內部的this,指向頁面實例。

(3)頁面實例的this.setData()方法,可以更改配置對象的data屬性,進而通過數據綁定機制,導致頁面上的全局變量發生變化。

開發者工具導入項目代碼,點擊按鈕後,頁面渲染結果如下。

可以看到,點擊按鈕以後,頁面的文字從"hello 張三"變成了"hello 李四"。

這個示例的完整代碼,可以查看代碼倉庫

這裡要強調一下,修改頁面配置對象的data屬性時,不要直接修改this.data,這不僅無法觸發事件綁定機制去變更頁面,還會造成數據不一致,所以一定要通過this.setData()去修改。this.setData()是一個很重要的方法,有很多細節,詳細介紹可以讀一下官方文檔

四、動態提示 Toast

小程序提供了很多組件和方法,用來增強互動效果。比如,每次操作後,都顯示一個動態提示,告訴用戶操作的結果,這種效果叫做 Toast。

打開home.js文件,為this.setData()加上第二個參數。


Page({
  data: {
    name: '張三'
  },
  buttonHandler(event) {
    this.setData({
      name: '李四'
    }, function () {
      wx.showToast({
        title: '操作完成',
        duration: 700
      });
    }),
  }
});

上面代碼中,this.setData()方法加入了第二個參數,這是一個函數,它會在頁面變更完畢後(即this.setData()執行完)自動調用。

這個參數函數內部,調用了wx.showToast()方法,wx是小程序提供的原生對象,所有客戶端 API 都定義在這個對象上面,wx.showToast()會展示微信內置的動態提示框,它的參數對象的title屬性指定提示內容,duration屬性指定提示框的展示時間,單位為毫秒。

開發者工具導入項目代碼,點擊按鈕後,頁面渲染結果如下。

過了700毫秒,提示框就會自動消失。

這個示例的完整代碼,可以查看代碼倉庫

五、對話框 Modal

下面,我們再用小程序提供的wx.showModal()方法,製作一個對話框,即用戶可以選擇"確定"或"取消"。

打開home.js文件,修改如下。


Page({
  data: {
    name: '張三'
  },
  buttonHandler(event) {
    const that = this;
    wx.showModal({
      title: '操作確認',
      content: '你確認要修改嗎?',
      success (res) {      
        if (res.confirm) {
          that.setData({
            name: '李四'
          }, function () {
             wx.showToast({
               title: '操作完成',
               duration: 700
             });
          });
        } else if (res.cancel) {
          console.log('用戶點擊取消');
        }
      }
    });
  }
});

上面代碼中,用戶點擊按鈕以後,回調函數buttonHandler()裡面會調用wx.showModal()方法,顯示一個對話框,效果如下。

wx.showModal()方法的參數是一個配置對象。title屬性表示對話框的標題(本例為"操作確認"),content屬性表示對話框的內容(本例為"你確認要修改嗎?"),success屬性指定對話框成功顯示後的回調函數,fail屬性指定顯示失敗時的回調函數。

success回調函數里面,需要判斷一下用戶到底點擊的是哪一個按鈕。如果參數對象的confirm屬性為true,點擊的就是"確定"按鈕,cancel屬性為true,點擊的就是"取消"按鈕。

這個例子中,用戶點擊"取消"按鈕後,對話框會消失,控制台會輸出一行提示信息。點擊"確定"按鈕後,對話框也會消失,並且還會去調用that.setData()那些邏輯。

注意,上面代碼寫的是that.setData(),而不是this.setData()。這是因為setData()方法定義在頁面實例上面,但是由於success()回調函數不是直接定義在Page()的配置對象下面,this不會指向頁面實例,導致this.setData()會報錯。解決方法就是在buttonHandler()的開頭,將this賦值給變量that,然後在success()回調函數里面使用that.setData()去調用。關於this更詳細的解釋,可以參考這篇教程

這個示例的完整代碼,可以查看代碼倉庫

今天的教程就到這裡,對於初學者來說,已經講了很多東西,可能需要慢慢消化。如果對網頁開發和 JavaScript 語言不熟悉,你也許會覺得不容易完全理解,不用擔心,初學者只需要知道加入腳本的方法,以及腳本可以達到的效果就可以了,後面做到實際的項目,慢慢就會加深理解。

有了腳本以後,就可以通過小程序 API,去調用微信的各種內置能力。下一篇教程將重點講解如何使用小程序 API。

(完)