WP DEVELOP

EP111. “可选:jQuery-free 版 Like.js(原生 JS + Axios)”

首页 WordPress 开发课程 LIKE / HEART 功能 · EP111
约 14 分钟· #EP111#LIKE / HEART 功能
🔒 登录后可标记已读

📌 并入 EP110 的提醒(实际代码修复):解决 EP108/EP109 提到的 PHP 警告——未点赞时 $existQuery->posts[0]->ID 访问的是不存在的数组项。single-professor.php.like-boxdata-like 属性要改成:

data-like="<?php if (isset($existQuery->posts[0]->ID)) echo $existQuery->posts[0]->ID; ?>"

isset() 先判断这个数组项存不存在,存在才输出,不存在就干脆不输出(属性值留空),避免 PHP 对着不存在的数组下标发警告。


跟 Search.js(EP084)、MyNotes.js(EP102)一样是同一模式的补录加课:提供一份不依赖 jQuery、用原生 JS + Axios 重写的 Like.js,可以直接整份替换。功能表现完全一致,作者提到这里没有引入任何新概念,纯粹是练习巩固前两次已经讲过的「原生 DOM API 替代 jQuery」的写法。


涉及文件

  • wp-content/themes/fictional-university-theme/single-professor.php (修改,isset() 修复)
  • wp-content/themes/fictional-university-theme/src/modules/Like.js (整份替换)

代码实现

完整的 jQuery-free 版本(课程资源直接提供下载,整份复制替换掉原文件):

// wp-content/themes/fictional-university-theme/src/modules/Like.js
import axios from "axios"

class Like {
  constructor() {
    if (document.querySelector(".like-box")) {
      axios.defaults.headers.common["X-WP-Nonce"] = universityData.nonce
      this.events()
    }
  }

  events() {
    document.querySelector(".like-box").addEventListener("click", e => this.ourClickDispatcher(e))
  }

  ourClickDispatcher(e) {
    let currentLikeBox = e.target
    while (!currentLikeBox.classList.contains("like-box")) {
      currentLikeBox = currentLikeBox.parentElement
    }

    if (currentLikeBox.getAttribute("data-exists") == "yes") {
      this.deleteLike(currentLikeBox)
    } else {
      this.createLike(currentLikeBox)
    }
  }

  async createLike(currentLikeBox) {
    try {
      const response = await axios.post(universityData.root_url + "/wp-json/university/v1/manageLike", { "professorId": currentLikeBox.getAttribute("data-professor") })
      if (response.data != "Only logged in users can create a like.") {
        currentLikeBox.setAttribute("data-exists", "yes")
        var likeCount = parseInt(currentLikeBox.querySelector(".like-count").innerHTML, 10)
        likeCount++
        currentLikeBox.querySelector(".like-count").innerHTML = likeCount
        currentLikeBox.setAttribute("data-like", response.data)
      }
      console.log(response.data)
    } catch (e) {
      console.log("Sorry")
    }
  }

  async deleteLike(currentLikeBox) {
    try {
      const response = await axios({
        url: universityData.root_url + "/wp-json/university/v1/manageLike",
        method: 'delete',
        data: { "like": currentLikeBox.getAttribute("data-like") },
      })
      currentLikeBox.setAttribute("data-exists", "no")
      var likeCount = parseInt(currentLikeBox.querySelector(".like-count").innerHTML, 10)
      likeCount--
      currentLikeBox.querySelector(".like-count").innerHTML = likeCount
      currentLikeBox.setAttribute("data-like", "")
      console.log(response.data)
    } catch (e) {
      console.log(e)
    }
  }
}

export default Like

关键改动点:

  • constructor 里先判断 document.querySelector(".like-box") 是否存在,只有教授详情页才会跑这段逻辑,其他页面直接跳过——这个「先判断相关元素存不存在」的写法在 EP102 的 MyNotes.js 里也用过,是这系列 jQuery-free 重写共同的习惯
  • nonce 同样是在 constructor 里用 axios.defaults.headers.common["X-WP-Nonce"] 全局设置一次,createLike()/deleteLike() 内部不用再单独处理
  • ourClickDispatcherwhile (!currentLikeBox.classList.contains("like-box")) { currentLikeBox = currentLikeBox.parentElement } 手写「向上找最近的 .like-box 祖先」逻辑,等价于 jQuery 版的 .closest(".like-box")
  • createLike() 里多了一层判断:if (response.data != "Only logged in users can create a like.") 才更新界面——因为用 Axios 时,只要 HTTP 状态码不是 2xx 才会走 catch,而这个端点在「未登录」时是用 die() 提前终止并返回一段文字(状态码可能依然是 200),所以要显式检查返回内容是不是那句拒绝文案,避免把「其实被拒绝了」误判成「点赞成功」
  • deleteLike() 用了 axios({ url, method: 'delete', data }) 这种更完整的对象写法,而不是 axios.delete(url, data) 的简写——这是因为 Axios 的 .delete() 简写方法在传递请求体(data)时的参数位置和普通的 GET/POST 简写不太一样,用完整对象写法可以更明确地把 data 传过去,不容易出错

Hook / Function 速查

本讲没有出现新的 WordPress hook 或 function,是对已学过的原生 JS 写法(querySelectorclassListsetAttributeparentElementaxios + async/await)的巩固练习,具体可参考 EP084、EP102 的速查表。


常见坑

  • 用 Axios 时以为服务端 die() 提前终止请求一定会让请求「失败」(触发 catch)——die() 只是提前结束 PHP 输出,HTTP 状态码不一定是错误码,前端必须显式检查返回内容而不能只依赖 try/catch 的成功与否来判断业务是否真的成功
  • isset() 判断只加在读取的地方、却忘了这本来是为了消除 PHP 警告,不是为了改变功能逻辑——修复后即使数组项不存在,属性值就是空字符串,不影响后续 JS 里 data-exists/data-like 的判断逻辑

Sources

Udemy:

  • Become a WordPress Developer: Unlocking Power With Code — Section 20, EP111(含 EP110 提醒并入)