阅读视图

发现新文章,点击刷新页面。

Vue中级冒险:3-4周成为组件沟通大师 🚀

欢迎使用我的小程序👇👇👇👇

small.png


欢迎回到你的Vue学习之旅!如果你已经跨过了基础门槛,那么接下来的3-4周将带你进入一个全新的世界——在这里,组件不再孤立,数据流动如交响乐般和谐,代码组织变得优雅而强大。

📅 第一周:与“时间魔法师”——生命周期成为好友

想象一下,每个Vue组件都像一个小生命,有自己的出生、成长和告别时刻。生命周期钩子就是这些关键时刻的提醒铃铛🔔。

// 以前你可能只认识created和mounted
// 现在来认识整个生命周期家族吧!
export default {
  beforeCreate() { console.log('我即将诞生!') },
  created() { console.log('我出生了!可以访问数据啦') },
  beforeMount() { console.log('准备挂载到DOM树上...') },
  mounted() { console.log('成功安家!现在可以操作DOM了') },
  beforeUpdate() { console.log('数据变了,我要准备更新啦') },
  updated() { console.log('更新完成!界面焕然一新') },
  beforeUnmount() { console.log('我即将离开这个世界...') },
  unmounted() { console.log('再见!清理工作完成') }
}

本周小挑战:写一个组件,在它生命的每个阶段都在控制台留下足迹👣,观察数据变化和DOM更新时的触发顺序。

📅 第二周:掌握组件间的“悄悄话”艺术

组件不会读心术,但它们有6种方式可以交流!让我们把它们想象成住在不同房间的室友:

1. Props:妈妈喊你吃饭式(父→子)

// 爸爸组件大喊:
<ChildComponent :dinner="'红烧肉'" />

// 孩子组件乖乖接收:
props: ['dinner']

2. $emit:孩子有事报告式(子→父)

// 孩子在房间里喊:
this.$emit('hungry', '想吃零食')

// 爸爸在外面监听:
<ChildComponent @hungry="handleHungry" />

3. Refs:直接敲门式

// 获取组件实例,直接调用方法
<ChildComponent ref="child" />
this.$refs.child.doSomething()

4. Event Bus:小区广播式(任意组件间)

// 创建一个中央事件总线
// 组件A:广播消息
eventBus.emit('news', '今天小区停水')

// 组件B:收听广播
eventBus.on('news', (msg) => {
  console.log(msg) // 今天小区停水
})

5. Provide/Inject:家族秘密传承式(跨层级)

// 爷爷辈组件:
provide() {
  return { familySecret: '传家宝的位置' }
}

// 孙子辈组件(跳过爸爸直接获取):
inject: ['familySecret']

6. Vuex/Pinia:社区公告栏式(全局状态)

// 任何组件都可以:
store.commit('setMessage', '社区通知:明天停电')

// 任何组件也都能看到:
store.state.message

本周实践:创建一个“家庭聊天室”应用,使用至少4种通信方式让祖孙三代的组件互相传递消息!

📅 第三、四周:解锁组合式API的“积木魔法”

还记得小时候搭积木的乐趣吗?组合式API让你重新体验这种快乐!

选项式API vs 组合式API

// 以前(选项式) - 像整理抽屉
export default {
  data() { return { count: 0 } },
  methods: { increment() { this.count++ } },
  computed: { doubleCount() { return this.count * 2 } }
}

// 现在(组合式) - 像搭积木
import { ref, computed } from 'vue'

export default {
  setup() {
    // 逻辑1:计数器
    const count = ref(0)
    const increment = () => { count.value++ }
    const doubleCount = computed(() => count.value * 2)
    
    // 逻辑2:用户信息
    const user = ref(null)
    const fetchUser = async () => { /* ... */ }
    
    // 像搭积木一样组合功能
    return { count, increment, doubleCount, user, fetchUser }
  }
}

超能力:自定义组合函数

// 创建一个可复用的“鼠标跟踪器”积木
import { ref, onMounted, onUnmounted } from 'vue'

export function useMouse() {
  const x = ref(0)
  const y = ref(0)
  
  const update = (event) => {
    x.value = event.pageX
    y.value = event.pageY
  }
  
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))
  
  return { x, y }
}

// 在任何组件中轻松使用:
const { x, y } = useMouse()
// 看!鼠标坐标自动跟踪!

响应式进阶:reactive vs ref

// ref - 给单个值加响应式外衣
const count = ref(0) // 访问时:count.value

// reactive - 给对象加响应式外衣
const state = reactive({ 
  name: 'Vue', 
  version: 3 
}) // 访问时:state.name

// 小贴士:简单值用ref,复杂对象用reactive

终极挑战:用组合式API重构你之前的一个项目,把相关逻辑抽成自定义组合函数,体验“代码乐高”的快乐!

🎉 庆祝时刻:你已经成为Vue中级开发者!

经过这3-4周的冒险,你已经掌握了:

  • 生命周期管理:像时间旅行者一样掌控组件的一生
  • 6种组件通信:让组件间的对话流畅自然
  • 组合式API:用乐高式思维构建可维护的代码

现在你的Vue技能树已经枝繁叶茂🌳!这些技能不仅在面试中闪闪发光,更能让你在实际项目中游刃有余。

下一步冒险预告:高级路由管理、性能优化、服务端渲染... 但先给自己放个小假,用新技能做个有趣的小项目吧!


分享你的学习成果或遇到的问题,在评论区一起交流成长!你的3周挑战故事是什么? 💬

#Vue #前端开发 #编程学习 #JavaScript #组合式API

Vue3 组件入门:像搭乐高一样玩转前端!

欢迎使用我的小程序👇👇👇👇

small.png


你好呀!如果你刚开始学习 Vue3 组件开发,那你来对地方了!想象一下,组件就像是前端世界的乐高积木——小巧、独立、可重复使用,还能组合成酷炫的东西。让我们花 1-2 周时间,轻松掌握组件开发的三大基石!

🎯 第一周:认识你的“乐高积木”

组件基本结构:Vue 的“基因代码”

每个 Vue 组件都像一个独立的小程序,有自己的模板、逻辑和样式:

<template>
  <!-- 这里是组件的“外貌” -->
  <div class="my-component">
    <h1>{{ title }}</h1>
    <button @click="handleClick">点我!</button>
  </div>
</template>

<script setup>
// 这里是组件的“大脑”
import { ref } from 'vue'

const title = ref('你好,我是组件!')

const handleClick = () => {
  console.log('按钮被点击啦!')
}
</script>

<style scoped>
/* 这里是组件的“穿搭” */
.my-component {
  border: 2px solid #42b983;
  padding: 20px;
  border-radius: 10px;
}
</style>

💡 小贴士<script setup> 是 Vue3 的语法糖,让代码更简洁!scoped 样式确保穿搭只影响自己,不会“撞衫”。

🔄 第二周:让积木“活”起来

Props:组件的“个性定制”

就像给乐高人仔换装一样,Props 让组件可以接收外部数据:

<!-- UserCard.vue -->
<template>
  <div class="user-card">
    <h2>{{ name }}</h2>
    <p>年龄:{{ age }}</p>
    <p v-if="isVip">⭐ VIP会员</p>
  </div>
</template>

<script setup>
// 定义组件可以接收哪些“定制参数”
const props = defineProps({
  name: {
    type: String,
    required: true  // 这个必须传!
  },
  age: {
    type: Number,
    default: 18     // 不传的话默认18岁
  },
  isVip: Boolean    // 简写形式
})
</script>

使用这个组件时:

<template>
  <UserCard name="小明" :age="20" :is-vip="true" />
  <UserCard name="小红" /> <!-- 小红自动18岁,不是VIP -->
</template>

🎭 有趣比喻:Props 就像点奶茶时的选项——甜度、冰度、加料,同一个奶茶组件,能调出千变万化的味道!

Events:组件的“悄悄话机制”

组件不能总是被动接收,有时也需要主动“说话”:

<!-- Counter.vue -->
<template>
  <div>
    <p>计数:{{ count }}</p>
    <button @click="increment">+1</button>
    <button @click="reset">归零</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const emit = defineEmits(['count-change', 'reset-done'])

const count = ref(0)

const increment = () => {
  count.value++
  // 对外“喊话”:计数变化啦!
  emit('count-change', count.value)
}

const reset = () => {
  count.value = 0
  // 喊另一句话:重置完成啦!
  emit('reset-done')
}
</script>

父组件接收“悄悄话”:

<template>
  <Counter 
    @count-change="onCountChange"
    @reset-done="showAlert('已归零!')"
  />
</template>

<script setup>
const onCountChange = (newCount) => {
  console.log(`计数器变成${newCount}了!`)
}

const showAlert = (msg) => {
  alert(msg)
}
</script>

🔊 生动解释:Events 就像组件之间的“对讲机”。子组件按下按钮,父组件就能听到:“嘿!我这里发生事情了!”

插槽:组件的“留白艺术”

有时候,我们想在组件里留一块空地,让使用它的人自由发挥:

<!-- FancyBox.vue -->
<template>
  <div class="fancy-box">
    <div class="header">
      <slot name="header">默认标题</slot>
    </div>
    
    <div class="content">
      <!-- 匿名插槽:不写name的那个 -->
      <slot>默认内容</slot>
    </div>
    
    <div class="footer">
      <slot name="footer"></slot>
      <!-- 如果没提供footer,这里什么都不显示 -->
    </div>
  </div>
</template>

尽情发挥创意:

<template>
  <FancyBox>
    <!-- #header 是 v-slot:header 的简写 -->
    <template #header>
      <h1>🎉 我的个性化标题!</h1>
    </template>
    
    <!-- 这里是匿名插槽的内容 -->
    <p>这是放在主区域的内容...</p>
    <img src="/my-image.jpg" alt="我的图片">
    
    <template #footer>
      <button>确定</button>
      <button>取消</button>
    </template>
  </FancyBox>
</template>

🎨 精妙比喻:插槽就像相框——相框组件提供结构和样式(边框、材质),但你可以在里面放任何照片!

🚀 两周学习路线图

第一周:打好地基

  • 第1-2天:创建你的第一个组件,理解“单文件组件”概念
  • 第3-4天:玩转 Props,尝试各种类型验证
  • 第5-7天:组件通信初体验,父子组件互相“对话”

第二周:进阶组合

  • 第8-10天:掌握具名插槽和作用域插槽
  • 第11-12天:构建一个小项目(如用户卡片集)
  • 第13-14天:重构重复代码为可复用组件

💪 动手挑战!

试着创建一个 MessageBubble 组件:

  1. 通过 type prop 控制样式(成功、警告、错误)
  2. 点击气泡时发射 close 事件
  3. 使用插槽让内容可以包含任何 HTML
  4. 添加一个 icon 插槽,允许自定义图标

🌟 总结

Vue3 组件开发其实就像玩乐高:

  • 基本结构 = 积木的基础形状
  • Props = 给积木涂上不同颜色
  • Events = 积木之间的连接卡扣
  • 插槽 = 预留的特殊接口

记住,最好的学习方式就是动手去做!从今天起,试着把页面上的每个部分都想象成可复用的组件。两周后,你会惊讶地发现,自己已经能用“乐高思维”构建整个应用了!

有什么问题或有趣的组件创意吗?欢迎在评论区分享!一起在 Vue3 的世界里搭出炫酷的作品吧!✨


📅 学习进度提醒:标记你的日历,两周后回来看看自己构建了多少个酷炫组件!

模块化开发:当CommonJS遇见ES Module

欢迎使用我的小程序👇👇👇👇

small.png


一、为什么我们需要模块化?

想象一下,你正在建造一座乐高城堡。如果所有的积木都混在一个大袋子里,每次要找特定零件都得翻遍整个袋子,这会是多么低效!早期的JavaScript开发就是这样——所有代码都写在一个或几个文件中,导致:

  • 变量污染全局作用域
  • 难以维护和调试
  • 代码复用困难
  • 依赖关系混乱

模块化就像把乐高积木按颜色、形状分类放在不同的盒子里,让搭建变得更有序、更高效。

二、CommonJS:Node.js的“老将”

基本概念

CommonJS是Node.js默认的模块系统,它采用同步加载的方式,非常适合服务器端环境。

核心语法

// 导出模块 - math.js
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;

// 方式1:导出单个对象
module.exports = { add, multiply };

// 方式2:逐个添加属性
exports.add = add;
exports.multiply = multiply;

// 导入模块 - app.js
const math = require('./math.js');
console.log(math.add(2, 3)); // 5

特点解析

同步加载:像在图书馆借书,必须等上一本书拿到手,才能借下一本

// 模块会立即执行
const fs = require('fs'); // 阻塞执行,直到fs模块完全加载
const data = fs.readFileSync('file.txt'); // 继续阻塞

动态导入:你可以在条件语句中导入模块

if (userNeedsAdvancedFeature) {
    const advancedModule = require('./advanced');
    // 使用模块
}

适用场景

  • Node.js后端开发
  • 构建工具(如Webpack、Browserify转换后可在浏览器使用)

三、ES Module:现代JavaScript的“新星”

基本概念

ES Module是ECMAScript 2015(ES6)引入的官方模块标准,采用异步加载,是现代前端开发的首选。

核心语法

// 导出模块 - math.js
export const add = (a, b) => a + b;
export const multiply = (a, b) => a * b;

// 默认导出(每个模块只能有一个)
const defaultFunction = () => console.log('默认导出');
export default defaultFunction;

// 导入模块 - app.js
import { add, multiply } from './math.js';
import myDefault from './math.js'; // 导入默认导出

// 重命名导入
import { add as sum } from './math.js';

// 整体导入
import * as math from './math.js';

特点解析

静态分析:像餐厅点餐,先看完整菜单再决定点什么

// 导入声明必须在顶层,不能在条件语句中
import { featureA } from './moduleA'; // ✓ 正确

if (condition) {
    import { featureB } from './moduleB'; // ✗ 错误!语法不允许
}

// 但可以使用动态导入函数
if (condition) {
    import('./moduleB').then(module => {
        // 使用module.featureB
    });
}

异步加载:多个模块可以并行加载

// 模块1和模块2可以同时加载
import { utils } from './utils.js';
import { api } from './api.js';

// 动态导入返回Promise
const loadModule = async () => {
    const module = await import('./dynamic-module.js');
    module.doSomething();
};

实时绑定:导入的是值的引用,而不是副本

// counter.js
export let count = 0;
export const increment = () => { count++; };

// app.js
import { count, increment } from './counter.js';

console.log(count); // 0
increment();
console.log(count); // 1(值被更新了!)

四、CommonJS vs ES Module:直接对比

特性 CommonJS ES Module
语法 require() / module.exports import / export
加载时机 运行时加载 编译时静态解析
加载方式 同步 异步
环境 主要为Node.js 浏览器和Node.js
值类型 值的拷贝 值的引用(实时绑定)
条件导入 支持 静态导入不支持,动态导入支持
循环依赖 支持但可能有问题 支持且更可靠
严格模式 默认非严格模式 默认严格模式

循环依赖示例对比

CommonJS的问题

// a.js
exports.loaded = false;
const b = require('./b.js');
console.log('在a中,b是', b);
exports.loaded = true;

// b.js
exports.loaded = false;
const a = require('./a.js'); // 此时a还没有完全加载
console.log('在b中,a是', a); // a.loaded为false
exports.loaded = true;

ES Module的处理

// a.js
import { bLoaded } from './b.js';
export let aLoaded = false;
aLoaded = true;

// b.js
import { aLoaded } from './a.js';
export let bLoaded = false;
bLoaded = true;
// 可以正常工作,但需要注意初始化顺序

五、在现代项目中如何使用?

Node.js中的使用

Node.js从v13.2.0开始稳定支持ES Module:

  1. 使用.mjs扩展名

    // module.mjs
    export const hello = () => "Hello ES Module!";
    
    // app.mjs
    import { hello } from './module.mjs';
    
  2. package.json中设置"type": "module"

    {
      "name": "my-app",
      "type": "module",
      "scripts": {
        "start": "node app.js"
      }
    }
    

浏览器中的使用

<!-- 直接使用ES Module -->
<script type="module">
  import { createApp } from './app.js';
  createApp();
</script>

<!-- 支持相对和绝对路径 -->
<script type="module" src="./modules/main.js"></script>

互操作性

在ES Module中导入CommonJS模块:

// 可以导入CommonJS模块
import commonJSModule from './commonjs-module.cjs';
import { namedExport } from './commonjs-module.cjs'; // 如果CommonJS模块有提供

// 在package.json中指定不同扩展名的处理方式

六、实践建议

  1. 新项目:优先使用ES Module,它是未来的标准
  2. Node.js项目
    • 新项目:使用ES Module(设置"type": "module"
    • 现有项目:逐步迁移或维持CommonJS
  3. 浏览器项目:使用ES Module,配合构建工具(如Webpack、Vite)
  4. 库/包开发:考虑双模式支持
    // package.json
    {
      "name": "my-library",
      "main": "./dist/commonjs/index.js",
      "module": "./dist/esm/index.js",
      "exports": {
        "require": "./dist/commonjs/index.js",
        "import": "./dist/esm/index.js"
      }
    }
    

七、常见问题解答

Q:我应该学习哪一个? A:两者都要了解!ES Module是未来,但很多现有项目使用CommonJS。

Q:可以在同一个文件中混用吗? A:尽量避免,但在Node.js中可以通过动态导入互相调用。

Q:哪个性能更好? A:ES Module的静态特性允许更好的优化和摇树(tree-shaking)。

总结

模块化让JavaScript从"玩具语言"成长为"工程语言"。CommonJS像是一位经验丰富的老兵,在Node.js生态中建立了坚实基础;ES Module则像是一位充满活力的新星,代表着JavaScript的未来方向。

记住一个简单的选择策略:

  • 前端开发 → ES Module
  • Node.js新项目 → ES Module
  • Node.js旧项目维护 → CommonJS
  • 通用库开发 → 考虑双模式支持

无论选择哪种方式,模块化的核心思想都是一致的:关注点分离、高内聚低耦合、代码复用。掌握了这些概念,你就掌握了现代JavaScript开发的钥匙。

模块化不是目的,而是手段。真正的目标是编写可维护、可扩展、高质量的代码。

❌