Vue3快速入门(二)

Vue3快速入门(二)

个人博客:Vue3快速入门(二)

Vue3快速入门(一)

1. 父子组件间的数据共享

1.1 props父传子

子组件

<template>
  <h2>{{props.name}}</h2>
  <!-- 可省略prop. -->
  <h2>{{name}}</h2>
</template>

<script setup>
  const props = defineProps({ // defineProps在<script setup>中可直接使用,无需引入
    name: {
      type: String,
      default: ''
    }
  })

  // 也可以用下面数组的形式
  // const props = defineProps(['name'])  
</script>

父组件

<template>
  <Son name="clz"></Son>
</template>


<script setup>
  import Son from './components/Son.vue' // 组件自动注册
</script>

image-20220301103045960

1.2 emit子传父

子组件

<template>
  <h2>子组件数据:{{msg}}</h2>
  <button @click="send">发送数据给父组件</button>
</template>

<script setup>
  const msg = "我是子组件的数据"

  const emit = defineEmits(['getMessage'])

  const send = (() => {
    emit('getMessage', msg)
  })
</script>

父组件

<template>
  <h2>子组件数据:{{msg}}</h2>
  <button @click="send">发送数据给父组件</button>
</template>

<script setup>
  const msg = "我是子组件的数据"

  const emit = defineEmits(['getMessage'])

  const send = (() => {
    emit('getMessage', msg)
  })
</script>

vue3

1.3 v-model实现父子互传

默认情况下,组件上的 v-model 使用 modelValue 作为 prop 和 update:modelValue 作为事件。可以通过向 v-model 传递参数来修改这些默认名称

子组件

<template>
  <h2>姓名:{{modelValue}}</h2>
  <button @click="changeName">改名</button>
  <h2>年龄:{{age}}</h2>
  <button @click="changeAge">年龄</button>
</template>

<script setup>
  defineProps({
    modelValue: String,
    age: Number
  })

  const emit = defineEmits(['update:modelValue', 'update:age'])
  const changeName = () => {
    emit('update:modelValue', '赤蓝紫')
  }

  const changeAge = () => {
    emit('update:age', 999)
  }
</script>

父组件

<template>
  <Son v-model="person.name" v-model:age="person.age"></Son>
</template>


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

  import Son from './components/Son.vue' // 组件自动注册

  const person = reactive({
    name: 'clz',
    age: 21
  })
</script>

vue3

1.4 子组件ref变量子传父

子组件

<template>
  <h2>{{name}}</h2>
</template>

<script>
  import {
    ref,
    toRefs
  } from 'vue'

  export default {
    setup() {
      const name = ref('clz')
      const changeName = () => {
        name.value = '赤蓝紫'
      }

      return {
        name,
        changeName
      }
    }
  }
</script>

父组件

<template>
  <Son ref="sonRef"></Son>
</template>


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

  import Son from './components/Son.vue' // 组件自动注册

  const sonRef = ref('sonRef')

  nextTick(() => {
    console.log(sonRef.value.name)

    setTimeout(() => {
      sonRef.value.changeName()
    }, 1000)

  })
</script>

vue3

上面会发现用回了setup函数,这是因为需要引入一下defineExpose

首先,变成<script setup>,发现无法得到相同效果,甚至会报错

<template>
  <h2>{{name}}</h2>
</template>
<script setup>
  import {
    ref,
    toRefs
  } from 'vue'

  const name = ref('clz')
  const changeName = () => {
    name.value = '赤蓝紫'
  }
</script>

这是因为,标准组件写法中,子组件的数据都默认隐式暴露给父组件了,而在<script setup>模式下,所有数据只是默认return给template使用而已,不会暴露到组件外,所以父组件无法直接通过挂载ref变量获取子组件的数据。

可以通过defineExpose将子组件显示暴露出来,父组件才能通过ref变量获取子组件的数据

<template>
  <h2>{{name}}</h2>
</template>
<script setup>
  import {
    ref,
    toRefs
  } from 'vue'

  const name = ref('clz')
  const changeName = () => {
    name.value = '赤蓝紫'
  }

  defineExpose({
    name,
    changeName
  })
</script>

1.5 provide和inject

使用provideinject,无论组件层次结构多深,父组件都可以作为其所有子组件的依赖提供者。

父组件通过provide来提供数据,子组件通过inject来使用这些数据

父组件

<template>
  <div class="father">
    <h2>父组件</h2>
    <Son></Son>
  </div>

</template>

<script>
// 父组件通过provide来提供数据,子组件通过inject来使用这些数据

import { reactive, toRefs, provide } from "vue"
import Son from "./components/Son.vue"

export default {
  name: "App",
  components: {
    Son
  },

  setup() {
    let person = reactive({ name: "赤蓝紫", age: 21 })
    provide("person", person) //给自己的后代组件传递数据
    return { ...toRefs(person) }
  }
}
</script>

<style scoped>
.father {
  width: 300px;
  height: 300px;
  background-color: #666;
}
</style>

子组件

<template>
  <div class="son">
    <h2>子组件</h2>
    {{person}}
  </div>
</template>

<script>
import { inject } from "vue"

export default {
  setup() {
    let person = inject("person")
    return { person }
  }
}
</script>

<style scoped>
.son {
  background-color: #eee;
}
</style>

image-20220220131546539

2. 插槽slot

子组件

<template>
  <!-- 匿名插槽 -->
  <slot />

  <!-- 具名插槽 -->
  <slot name="title" />

  <!-- 作用域插槽 -->
  <slot name="footer" :scope="person" />
</template>

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

  const person = reactive({
    name: 'clz',
    age: 21
  })
</script>

父组件

<template>
  <Son>
    <h2 class="default">匿名插槽</h2>

    <template #title>
      <h2 class="name">具名插槽</h2>
      <h2 class="name">具名插槽</h2>
    </template>

    <template #footer="{ scope }">
      <h2 class="area">作用域插槽</h2>
      <footer class="area">姓名:{{scope.name}}</footer>
      <footer class="area">年龄{{scope.age}}</footer>
    </template>
  </Son>
</template>

<script setup>
  import Son from './components/Son.vue'
</script>

<style scoped>
  .default {
    color: red;
  }

  .name {
    color: blue;
  }

  .area {
    color: purple;
  }
</style>

image-20220221002538205

3. CSS变量注入

<template>
  <h2>CLZ</h2>
</template>
<script setup>
  import {
    ref
  } from 'vue'

  const color = ref('red')
</script>

<style scoped>
  h2 {
    /* 使用v-bind绑定变量 */
    color: v-bind('color')
  }
</style>

4. 定义组件的name

用单独的<script>块定义即可

<template>
  <h2>CLZ</h2>
</template>
<script setup>
  import {
    ref
  } from 'vue'

  const color = ref('red')
</script>

<script>
  export default {
    name: 'Test'
  }
</script>

<style scoped>
  h2 {
    /* 使用v-bind绑定变量 */
    color: v-bind('color')
  }
</style>

image-20220221001111236

5. Vuex的简单使用

之前在字节的青训营里,有老师建议不要再学Vuex了,要就学pinia。然后实习要先搞一手老项目,用的就是Vuex,自然还是得学一下的。当然,之后再学一下pinia,看能不能自己悟出来,为什么老师更建议pinia

5.1 介绍

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

状态管理模式:

  • 状态:驱动应用的数据源
  • 视图:以声明方式将状态映射到视图
  • 操作:响应在视图上的用户输入导致的状态变化

image-20220221220827405

但是,当需要多个组件共享状态时,单向数据流的简洁性很容易被破坏。因为:

  • 多个视图依赖于同一状态
  • 来自不同视图的行为需要变更同一状态

Vuex就是将组件的共享状态抽离出来,是代码变得更结构化且易维护

5.2 State和Mutation

State:存储共享状态

Mutation: 修改状态。更改Vuex的store中的状态的唯一方法是提交 mutation

store \ index.js

import { createStore } from 'vuex'

const store = createStore({
  state() {		// 存储状态
    return {
      count: 0
    }
  },
  // 或下面的形式,注意:() => ({}), 右边有括号包住。因为要说明右面是对象形式,而不是函数形式
  // state: () => ({
  //   count: 0
  // }),
  mutations: {		//  修改状态。更改Vuex的store中的状态的唯一方法是提交 mutation
    increment(state) {
      state.count++
    }
  }
})

export default store

main.js

import { createApp } from 'vue'
import App from './App.vue'
import store from './store'

createApp(App)
  .use(store)
  .mount('#app')

App.vue

<template>
  <h2>{{store.state.count}}</h2>
  <button @click="add">+1</button>
  <h2>---------以下是子组件----------</h2>
  <Son></Son>
</template>

<script setup>
  import {
    useStore
  } from 'vuex'

  import Son from './components/Son.vue'

  const store = useStore()			// 通过useStore()获取store,类似于useRoute(),之前是通过this.$store

  const add = () => {
    store.commit('increment')		// 通过store.commit()提交 mutation, 修改状态
     
    // 可以携带参数,只要mutations下的函数有两个参数,第二个便是携带的参数
    // increment(state, n) {
    //     state.count += n
    // }
    // store.commit('increment', 10)
  }
</script>

components \ Son.vue

<template>
  <h2>{{store.state.count}}</h2>

</template>

<script setup>
  import {
    useStore
  } from 'vuex'

  const store = useStore()
</script>

vue3

又有问题了,不通过store.commit()来修改,而是通过store.state.***来修改,会发现展示的效果不一样,但是呢,实际上存储的状态那边是没有更新的,所以状态的修改必须通过mutations下的方法修改。下面就来瞧一瞧是不是真的。

add方法变化:

const add = () => {
  store.state.count++
}

vue3

可以发现,状态确实没有发生变化,只是视图发生了变化

5.3 Getter

Getter:访问State存储的状态

5.3.1 通过属性访问

Getter 会暴露为 store.getters 对象,可以以属性的形式访问这些值:

store.getters.count

store \ index.js

import { createStore } from 'vuex'

const store = createStore({
  state() {
    return {
      count: 0
    }
  },
  getters: {
    count: (state) => {
      return state.count
    }
  },
  mutations: {
    increment(state) {
      state.count++
    }
  }
})

export default store

main.js

import { createApp } from 'vue'
import App from './App.vue'
import store from './store'

createApp(App)
  .use(store)
  .mount('#app')

App.vue

<template>
   <!--  注意这里 -->
  <h2>{{store.getters.count}}</h2>

  <button @click="add">+1</button>
  <h2>---------以下是子组件----------</h2>
  <Son></Son>
</template>

<script setup>
  import {
    useStore
  } from 'vuex'

  import Son from './components/Son.vue'

  const store = useStore()

  const add = () => {
    store.commit('increment')
  }
</script>

components \ Son.vue

<template>
   <!--  注意这里 -->
  <h2>{{store.getters.count}}</h2>

</template>

<script setup>
  import {
    useStore
  } from 'vuex'

  const store = useStore()
</script>

正常结果,Vue Devtools下也实时更新

5.3.2 通过方法访问

通过让 getter 返回一个函数,来实现给 getter 传参。

如:

getters: {
  getTodo: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
},

image-20220222111921712

image-20220222111935625

5.4 Action

Action:Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。
5.4.1 简单使用

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.statecontext.getters 来获取 state 和 getters。

Action通过store.dispatch方法触发

store.dispatch('increment')

image-20220222121825636

5.4.2 Action支持异步

为什么要通过Action提交mutation呢?都直接提交不行吗?

这是因为mutation必须同步执行,而Action不受约束

mutation异步:在 mutation 中使用异步更新状态,虽然也会使状态正常更新,但是会导致开发者工具有时无法追踪到状态的变化,调试起来就会很困难

image-20220222122627684

vue3

视图更新,存储的状态没有实时更新

action异步

image-20220222123511145

vue3

5.5 Module

Module:Vuex允许将store分割成模块(module)。每个模块拥有自己的statemutationgetteraction,以及嵌套子模块

const moduleA = {
  state: () => ({
    count: 0
  }),
  mutations: {
    increment(state) {
      // 这里的 `state` 对象是模块的局部状态
      state.count++
    }
  },
  getters: { ... },
  actions: {
    // 对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState:
    add({ state, commit, rootState }) {
      console.log(state.count)
      console.log(rootState.count)

      commit('increment')
    }
  }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = createStore({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
5.2.1 示例

store \ module \ user.js

const user = {
  state: () => ({
    name: 'clz',
    age: 21
  }),
  mutations: {
    increment(state) {
      state.age++
    }
  },
  getters: {
    doubleAge(state) {
      return state.age * 2
    }
  },
  actions: {
    add({ state, commit, rootState }) {
      console.log(state.name)
      console.log(rootState.number)

      commit('increment')
    }
  }
}

export default user

store \ index.js

import { createStore } from 'vuex'

import user from './module/user'

const store = createStore({
  state() {
    return {
      number: 10
    }
  },
  modules: {
    user: user
  }
})

export default store

App.vue

<template>
  <h2>姓名: {{ user.name }}</h2>
  <h2>年龄: {{ user.age }}</h2>
  <button @click="add">年龄+1</button>
</template>

<script setup>
import {
  useStore
} from 'vuex'

const store = useStore()
const user = store.state.user

const add = () => {
  store.dispatch('add')
}
</script>

main.js

import { createApp } from 'vue'
import App from './App.vue'
import store from './store/index'

createApp(App)
  .use(store)
  .mount('#app')

image-20220222211011517

学习参考链接Vue3.2 setup语法糖、Composition API归纳总结

Vuex

### Vue3 快速入门教程 #### 1. 环境搭建 Vue3 的开发环境通常基于 Vite 构建工具,通过 `create-vue` 脚手架来快速初始化项目。以下是具体的实现方式: ```bash npm create vue@latest my-project cd my-project npm install npm run dev ``` 这一步骤能够帮助开发者迅速配置好一个支持 Vue3 的现代前端开发环境[^2]。 --- #### 2. 数据绑定 (Data Binding)Vue3 中,数据可以通过模板语法直接绑定到视图层。例如,可以每秒更新一次消息内容: ```html <template> <div>{{ message }}</div> </template> <script> import { ref, onMounted } from &#39;vue&#39;; export default { setup() { const message = ref(&#39;初始消息&#39;); onMounted(() => { setInterval(() => { message.value = Math.random().toString(36).substring(7); }, 1000); }); return { message }; } }; </script> ``` 上述代码展示了如何利用组合式 API (`setup`) 和响应式变量 (`ref`) 来动态修改页面上的内容[^3]。 --- #### 3. 动态样式 (Dynamic Style) 为了实现动态样式的功能,可以结合 Vue 的计算属性或者事件监听器完成颜色随机切换的效果: ```html <template> <div :style="{ backgroundColor: color }"> {{ message }} </div> <button @click="changeColor">点击更换背景色</button> </template> <script> import { ref } from &#39;vue&#39;; export default { setup() { const color = ref(&#39;red&#39;); const changeColor = () => { const colors = [&#39;blue&#39;, &#39;green&#39;, &#39;yellow&#39;, &#39;purple&#39;]; color.value = colors[Math.floor(Math.random() * colors.length)]; }; return { color, changeColor }; }, }; </script> ``` 此示例中,`:style` 绑定了动态的 CSS 属性,而按钮点击触发了颜色的变化逻辑。 --- #### 4. 条件渲染 (Conditional Rendering) Vue 提供了多种条件渲染的方式,常见的有 `v-if` 和 `v-show`。下面是一个简单的例子展示如何根据布尔值显示/隐藏组件: ```html <template> <div v-if="isVisible">这是可见的内容。</div> <button @click="toggleVisibility">切换可见性</button> </template> <script> import { ref } from &#39;vue&#39;; export default { setup() { const isVisible = ref(true); const toggleVisibility = () => { isVisible.value = !isVisible.value; }; return { isVisible, toggleVisibility }; }, }; </script> ``` 这里使用了 `v-if` 指令以及事件处理程序实现了交互效果。 --- #### 5. 生命周期钩子 (Lifecycle Hooks) 生命周期钩子允许开发者在特定的时间点执行自定义逻辑。例如,在组件挂载完成后打印一条日志信息: ```javascript import { onMounted } from &#39;vue&#39;; export default { setup() { onMounted(() => { console.log(&#39;组件已成功加载!&#39;); }); }, }; ``` 这段代码片段说明了如何借助 `onMounted` 钩子函数完成初始化操作。 --- #### 总结 以上介绍了 Vue3 的基本用法及其核心概念,包括但不限于数据绑定、动态样式、条件渲染和生命周期管理等内容。这些基础知识对于初学者来说至关重要,同时也是构建复杂应用的基础。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值