查看: 254|回复: 0

Vue2组件通信与生命周期:计算属性、watch及动态组件

[复制链接]
发表于 3 小时前 | 显示全部楼层 |阅读模式
在Vue2项目里,组件化不只是拆模板,核心是数据派生、副作用、生命周期和通信边界。原文以Node.js运行环境为背景,使用axios@1.7.3请求Flask的/films接口,并演示computed、watch、生命周期、props/$emit、ref、动态组件和slot。下面按可落地实现重组。

Node.js是JavaScript运行环境和包管理环境,node可运行JavaScript文件或进入交互式命令行,npm用于安装依赖,TypeScript增加类型系统后通常编译成浏览器可执行的JavaScript。Vue、React等前端框架通常依赖Node.js完成依赖安装、代码打包和开发服务器启动;Node.js不等于Vue,也不一定要作为业务后端,它首先是JavaScript运行环境。

一、axios请求电影数据与CORS
Vue页面用axios.get('http://127.0.0.1:5000/films')获取JSON,response.data才是后端返回的响应体;loading用于防止重复点击,try/catch/finally让成功、失败和结束状态都得到处理,列表渲染使用后端稳定id作为key。
  1. <div id="app">
  2.   <button @click="loadFilms" :disabled="loading">{{ loading ? '加载中...' : '加载电影' }}</button>
  3.   <p v-if="error">{{ error }}</p>
  4.   <ul>
  5.     <li v-for="film in films" :key="film.id">{{ film.name }}</li>
  6.   </ul>
  7. </div>
  8. <script src="https://unpkg.com/axios@1.7.3/dist/axios.min.js"></script>
  9. <script src="./vue2/vue.js"></script>
  10. <script>
  11. new Vue({
  12.   el: '#app',
  13.   data: { films: [], loading: false, error: '' },
  14.   methods: {
  15.     async loadFilms() {
  16.       this.loading = true
  17.       this.error = ''
  18.       try {
  19.         const response = await axios.get('http://127.0.0.1:5000/films')
  20.         this.films = response.data.results || []
  21.       } catch (err) {
  22.         console.error(err)
  23.         this.error = '电影数据加载失败,请检查接口服务。'
  24.       } finally {
  25.         this.loading = false
  26.       }
  27.     }
  28.   }
  29. })
  30. </script>
复制代码
Flask接口读取films.json并jsonify返回,演示跨域时设置Access-Control-Allow-Origin为http://localhost:8080;生产环境应配置明确域名,*不适合需要携带Cookie的请求,也不应在生产环境无限制开放。页面和接口的协议、域名或端口不一致时,浏览器会执行同源策略检查,服务端需要通过CORS响应头声明允许的来源。
  1. import json
  2. from flask import Flask, jsonify
  3. app = Flask(__name__)
  4. @app.get("/films")
  5. def films():
  6.     with open("films.json", encoding="utf-8") as file:
  7.         data = json.load(file)
  8.     response = jsonify(data)
  9.     response.headers["Access-Control-Allow-Origin"] = "http://localhost:8080"
  10.     return response
  11. if __name__ == "__main__":
  12.     app.run(port=5000)
复制代码

二、computed:带缓存的派生值
computed本质上是一个带缓存的派生值,依赖的数据变化时才重新计算,依赖不变时会复用上次结果,模板中像普通属性一样使用,不需要写括号。它适合格式化文本、过滤列表、统计总价等由已有数据推导出的值。与方法相比,计算属性会缓存结果;不要为了展示派生值把过滤结果重复存进data。
  1. new Vue({
  2.   el: '#app',
  3.   data: {
  4.     search: '',
  5.     words: ['a', 'at', 'atom', 'atommon', 'be', 'beyond', 'cs', 'csrf']
  6.   },
  7.   computed: {
  8.     filteredWords() {
  9.       const keyword = this.search.toLowerCase()
  10.       return this.words.filter(word => word.includes(keyword))
  11.     }
  12.   }
  13. })
复制代码
模板中用v-model.trim="search"和v-for="word in filteredWords",key用word。首字母大写示例通过charAt(0).toUpperCase()+slice(1)实现,name为空则返回空串。

三、watch:执行副作用
watch用来监听数据变化并执行副作用,例如发送请求、保存草稿、记录日志或根据一个字段修改另一个字段;computed返回结果,watch执行动作。监听对象或数组内部变化时,Vue2需要使用deep:true;初始化时就希望执行一次,可以使用immediate:true。深度监听成本更高,应只监听确实需要的对象。
  1. watch: {
  2.   category(newValue, oldValue) {
  3.     console.log(`${oldValue} -> ${newValue}`)
  4.     // 这里可以根据新分类请求后端数据
  5.   }
  6. }
复制代码
点击按钮修改category为“国产”“日韩”“欧美”,watch即可拿到新旧值并执行相应动作。

四、生命周期与资源清理
created可发初始化请求,mounted在DOM挂载后操作DOM;beforeDestroy、destroyed用于清理定时器、事件监听和资源。created中还不能可靠访问已经挂载的DOM;需要操作DOM尺寸、焦点等内容时使用mounted。组件销毁前不清理setInterval,定时器会继续执行。
  1. Vue.component('child-panel', {
  2.   template: `<section><h2>{{ name }}</h2><button @click="name = '新名字'">修改名字</button></section>`,
  3.   data() { return { name: '子组件', timerId: null } },
  4.   created() {
  5.     this.timerId = setInterval(() => { console.log('定时任务') }, 3000)
  6.   },
  7.   mounted() { console.log('mounted:DOM 已插入页面') },
  8.   beforeDestroy() {
  9.     clearInterval(this.timerId)
  10.     this.timerId = null
  11.   }
  12. })
复制代码
配合v-if控制子组件显示和销毁,可观察组件创建与销毁时的生命周期日志。

五、组件注册与父子通信
组件data必须是返回对象的函数,保证每个组件实例拥有独立数据。Vue.component注册全局组件,注册后所有Vue实例和子组件都可以使用;new Vue的components注册局部组件,只能在当前实例模板中使用。实际.vue单文件组件项目更常使用import/export管理组件,全局注册虽然方便,但会增加命名冲突和维护成本。
父传子用props,HTML属性通常使用短横线user-name,在组件JavaScript中对应驼峰userName;:title表示绑定变量,title="固定文本"则表示传入字符串常量。Props是单向数据流,子组件不应直接修改父组件传入的值。
  1. Vue.component('child-card', {
  2.   props: {
  3.     title: { type: String, required: true },
  4.     userName: { type: String, default: '' }
  5.   },
  6.   template: `<section><h2>{{ title }}</h2><p>用户:{{ userName }}</p></section>`
  7. })
  8. new Vue({
  9.   el: '#app',
  10.   data: { pageTitle: '父组件传来的标题' }
  11. })
复制代码
父模板中使用<child-card :title="pageTitle" user-name="lqz"></child-card>。子传父用$emit触发事件并传递数据,父组件使用@事件名监听;$emit的事件名要与父组件监听的名称一致。复杂项目中可以进一步使用状态管理工具,避免组件层级过深时逐层传递。
  1. Vue.component('child-editor', {
  2.   data() { return { text: '' } },
  3.   template: `<div><input v-model="text"><button @click="$emit('submit-message', text)">发送</button></div>`
  4. })
  5. new Vue({
  6.   el: '#app',
  7.   data: { message: '' },
  8.   methods: {
  9.     receiveMessage(value) { this.message = value }
  10.   }
  11. })
复制代码
父模板用<child-editor @submit-message="receiveMessage"></child-editor>接收数据。

六、ref、动态组件与slot
ref放在普通标签上得到DOM对象,放在组件上得到组件实例,适合聚焦输入框、调用子组件暴露的方法等少量命令式操作,不应替代正常的props和事件通信;$refs只有在组件挂载后才可用,不能在created中依赖,直接修改子组件内部数据会提高耦合度。动态组件通过<component :is="组件名或组件对象">切换要渲染的组件,适合选项卡、步骤表单和不同状态页面;旧组件可能被销毁,如果希望保留组件内部输入内容和滚动状态,可以使用<keep-alive>包裹动态组件,并结合activated、deactivated钩子处理缓存状态。插槽让父组件向子组件提供模板内容,子组件负责定义布局,适合封装卡片、弹窗、布局容器等结构固定但内容可变的组件;默认插槽使用<slot>,具名插槽通过name区分。
  1. Vue.component('base-card', {
  2.   template: `<section class="card"><header><slot name="header">默认标题</slot></header><main><slot>默认内容</slot></main></section>`
  3. })
  4. new Vue({ el: '#app' })
复制代码
父组件在<base-card>内传入h2和p内容。动态组件切换示例中,current在home-panel与profile-panel之间变化,<component :is="current"></component>渲染对应组件。

总结:computed用于缓存派生值,watch用于执行副作用;created中可请求数据,mounted中操作DOM,销毁钩子中清理定时器和监听器;父子通信优先使用props与$emit,ref仅做少量命令式操作,动态组件负责切换,插槽负责布局复用。可用电影列表、选项卡、父子消息传递和可复用卡片四个练习验证这些边界。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-25 16:17 , Processed in 0.022269 second(s), 29 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部