vue3.0(六) toRef,toValue,toRefs和toRow,markRaw


toRef

  • toRef 函数可以将一个响应式对象的属性转换为一个独立的 ref 对象。
  • 返回的是一个指向源对象属性的 ref 引用,任何对该引用的修改都会同步到源对象属性上。
  • 使用 toRef 时需要传入源对象和属性名作为参数。
  • 转换后的响应式可以被用于计算属性及监听器中
  1. 原对象是响应式,改变后,值改变,页面也会更新
    <template>
      <div>{{ state.bar }}</div>
      <div>{{ barState }}</div>
      <button @click="mutateDeeply">修改数据</button>
    </template>
    <script lang="ts">
    import { defineComponent, reactive, toRef } from 'vue'
    export default defineComponent({
      setup () {
        const state = reactive({
          bar: 2
        })
        const barState = toRef(state, 'bar')
        function mutateDeeply () {
          barState.value++
          console.log(barState.value, state.bar)
        }
        return {
          mutateDeeply,
          state,
          barState
        }
      }
    })
    </script>
    

上述代码执行效果

  1. 原对象为非响应式,改变后,值会改变,但页面不会更新
    <template>
      <div>{{ state.bar }}</div>
      <div>{{ barState }}</div>
      <button @click="mutateDeeply">修改数据</button>
    </template>
    <script lang="ts">
    import { defineComponent, reactive, toRef } from 'vue'
    export default defineComponent({
      setup () {
        const state = {
          bar: 2
        }
        const barState = toRef(state, 'bar')
        function mutateDeeply () {
          barState.value++
          console.log(barState.value, state.bar)
        }
        return {
          mutateDeeply,
          state,
          barState
        }
      }
    })
    </script>
    

上述代码执行效果

  1. 接收一个 Reactive 数组,此时第二个参数应该传入数组的下标:
  // 这一次声明的是数组
 const words = reactive([1, 2, 3])
  
  // 通过下标 `0` 转换第一个 item
  const a = toRef(words, 0)
  console.log(a.value) // 1
  console.log(words[0]) // 1
    
  // 通过下标 `2` 转换第三个 item
  const c = toRef(words, 2)
  console.log(c.value) // 3
  console.log(words[2]) // 3
  1. 设置默认值
  • 如果 Reactive 对象上有一个属性本身没有初始值,也可以传递第三个参数进行设置(默认值仅对 Ref 变量有效):
  • 数组也是同理,对于可能不存在的下标,可以传入默认值避免项目的逻辑代码出现问题:
    interface Member {
      id: number
      name: string
      // 类型上新增一个属性,因为是可选的,因此默认值会是 `undefined`
      age?: number
    }
     
    // 声明变量时省略 `age` 属性
    const userInfo: Member = reactive({
      id: 1,
      name: 'Petter',
    })
     
    // 此时为了避免程序运行错误,可以指定一个初始值
    // 但初始值仅对 Ref 变量有效,不会影响 Reactive 字段的值
    const age = toRef(userInfo, 'age', 18)
    console.log(age.value)  // 18
    console.log(userInfo.age) // undefined
     
    // 除非重新赋值,才会使两者同时更新
    age.value = 25
    console.log(age.value)  // 25
    console.log(userInfo.age) // 25
    
    const numbers = reactive([1, 2, 3])
     
    // 当下标对应的值不存在时,也是返回 `undefined`
    const d = toRef(words, 3)
    console.log(d.value) // undefined
    console.log(words[3]) // undefined
     
    // 设置了默认值之后,就会对 Ref 变量使用默认值, Reactive 数组此时不影响
    const e = toRef(words, 4, 'e')
    console.log(e.value) // e
    console.log(words[4]) // undefined
    

toValue

  • toValue() 是一个在 3.3 版本中新增的 API。它的设计目的是将 ref 或 getter 规范化为值。如果参数是 ref,它会返回 ref 的值;如果参数是函数,它会调用函数并返回其返回值
  • 与 unref() 类似,不同的是此函数也会规范化 getter 函数。如果参数是一个 getter,它将会被调用并且返回它的返回值。
  1. 应用
    console.log(toValue(1), toValue(ref(1)), toValue(() => 1)) // 1 1 1
    
  2. 在组合式函数中规范化参数:
<template>
  <button @click="mutateDeeply">修改数据</button>
</template>
<script lang="ts">
import { defineComponent, toValue, ref, watch } from 'vue'
import type { MaybeRefOrGetter } from 'vue'
export default defineComponent({
  setup () {
    const num = ref(2) 
    function mutateDeeply () {
      num.value++
      useFeature(num)
    }

    function useFeature (id: MaybeRefOrGetter<number>) {
      // 监听数据变化
      watch(() => toValue(id), newId => {
        // 处理 id 变更
        console.log(newId)
      })
    }
    // 这个组合式函数支持以下的任意形式:
    useFeature(1)
    useFeature(ref(1))
    useFeature(() => 1)
    return {
      mutateDeeply,
      num
    }
  }
})
</script>

toRefs

  • toRefs 函数可以将一个响应式对象转换为一个普通的对象,该对象的每个属性都是独立的 ref 对象。
  • 返回的对象可以进行解构,每个属性都可以像普通的 ref 对象一样访问和修改,而且会保持响应式的关联。
  • toRefs 的使用场景主要是在将响应式对象作为属性传递给子组件时,确保子组件可以正确地访问和更新这些属性。
  1. 与 toRef 不同, toRefs 只接收了一个参数,是一个 reactive 变量。
    <script lang="ts">
    import { defineComponent, toRefs, reactive } from 'vue'
    
    export default defineComponent({
      setup () {
        interface user {
          id: number
          name: string
        }
        
        // 声明一个 Reactive 变量
        const userInfo: user = reactive({
          id: 1,
          name: 'Petter'
        })
        
        // 传给 `toRefs` 作为入参
        const userInfoRefs = toRefs(userInfo)
        console.log(userInfoRefs)
        console.log(userInfoRefs.id.value)
        console.log(userInfoRefs.name.value)
        return {
        }
      }
    })
    </script>
    

上述代码打印结果
可以参考vue3.0(五) reactive全家桶

toRow

Vue 创建的代理返回其原始对象。

toRaw() 可以返回由 reactive()、readonly()、shallowReactive() 或者 shallowReadonly() 创建的代理对应的原始对象。
是一个可以用于临时读取而不引起代理访问/跟踪开销,或是写入而不触发更改的特殊方法
不建议保存对原始对象的持久引用,请谨慎使用。

import { reactive, toRaw } from 'vue'
const state = reactive({
 count: 0
})
// 获取原始对象
const rawState = toRaw(state)
// 验证原始对象与包装后的对象是否相等
console.log(rawState === state) // false
// 改变原始对象的值
rawState.count += 1
// 验证包装后的对象是否也受到了改变

markRaw

将一个对象标记为不可被转为代理。返回该对象本身。
markRaw 方法可以将一个对象标记为“非响应式”,从而使其不会被 reactive 包裹,也就不会成为 Vue3 中的响应式对象。

import { reactive, markRaw } from 'vue'
 
const state = reactive({
  count: 0,
  obj: {
    name: '张三'
  }
})
 
// 标记 obj 对象为“非响应式”
markRaw(state.obj)
// obj 对象不再被 reactive 包裹
console.log(state.hasOwnProperty('__v_raw')) // false
 
// 赋新值时不会触发响应更新
state.obj.name = '李四'
console.log(state.obj.name) // 李四

需要注意的是,一旦一个对象被标记为“非响应式”,它就无法再被 reactive 进行包裹成为响应式对象。
在使用 markRaw 方法时,需要确保这个对象在后续的代码中不需要作为响应式对象来使用或者监听其变化

toRef和toRefs的区别

  1. toRef 修改的是对象的某个属性,生成一个单独的 ref 对象。
  2. toRefs 修改的是整个对象,生成多个独立的 ref 对象集合。
  3. toRefs 适用于在组件传递属性或解构时使用,更加方便灵活,而 toRef 更适合提取单个属性进行操作。
  4. toRef 和 toRefs 都用于将响应式对象的属性转换为 ref 对象。
  5. 转换后的属性仍然保持响应式,对属性的修改会反映到源对象上。
  6. 不管是使用 toRef 还是 toRefs 将响应式对象转成普通对象,在 script 中修改和访问其值都需要通过 .value 进行。

toRaw 和markRaw的用处

  1. toRaw 可以还原数据并避免意外的响应式行为,
  2. markRaw 可以更加精细地控制哪些对象使用响应式并避免出现问题。
  • 9
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的购物车应用程序,使用Vue.js和Node.js进行构建: 前端: 1. 创建Vue.js项目:可以使用Vue CLI进行创建,输入以下命令: ``` vue create shopping-cart ``` 2. 安装必要的依赖项: ``` npm install axios vue-router vuex bootstrap-vue ``` 3. 创建组件: - App.vue:主组件,包含路由和导航栏 - Home.vue:主页组件,用于显示产品列表,并允许用户将产品添加到购物车 - Cart.vue:购物车组件,用于显示已添加到购物车的产品,并允许用户更改数量或删除产品 4. 创建路由: 在main.js文件中,使用以下代码创建路由: ```javascript import VueRouter from 'vue-router' import Home from './components/Home.vue' import Cart from './components/Cart.vue' const routes = [ { path: '/', component: Home }, { path: '/cart', component: Cart } ] const router = new VueRouter({ routes }) new Vue({ router, render: h => h(App), }).$mount('#app') ``` 5. 创建Vuex store: 在store.js文件中,使用以下代码创建store: ```javascript import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { cart: [] }, mutations: { addToCart(state, product) { const item = state.cart.find(item => item.id === product.id) if (item) { item.quantity++ } else { state.cart.push({...product, quantity: 1}) } }, removeFromCart(state, product) { const index = state.cart.findIndex(item => item.id === product.id) state.cart.splice(index, 1) }, updateQuantity(state, {product, quantity}) { const item = state.cart.find(item => item.id === product.id) item.quantity = quantity } }, actions: { addToCart({commit}, product) { commit('addToCart', product) }, removeFromCart({commit}, product) { commit('removeFromCart', product) }, updateQuantity({commit}, {product, quantity}) { commit('updateQuantity', {product, quantity}) } }, getters: { cartTotal(state) { return state.cart.reduce((total, item) => total + item.price * item.quantity, 0) }, cartItemCount(state) { return state.cart.reduce((total, item) => total + item.quantity, 0) } } }) ``` 6. 创建API服务: 使用Node.js和Express创建一个简单的API,用于获取产品列表: ```javascript const express = require('express') const app = express() const products = [ { id: 1, name: 'Product 1', price: 10 }, { id: 2, name: 'Product 2', price: 20 }, { id: 3, name: 'Product 3', price: 30 }, { id: 4, name: 'Product 4', price: 40 } ] app.get('/api/products', (req, res) => { res.json(products) }) app.listen(3000, () => { console.log('Server listening on port 3000') }) ``` 7. 使用Axios获取数据: 在Home.vue组件中,使用Axios获取产品列表,并将其显示在页面上: ```javascript <template> <div class="container"> <div class="row"> <div class="col-sm-4" v-for="product in products" :key="product.id"> <div class="card"> <div class="card-body"> <h5 class="card-title">{{ product.name }}</h5> <p class="card-text">{{ product.price }}</p> <button class="btn btn-primary" @click="addToCart(product)">Add to Cart</button> </div> </div> </div> </div> </div> </template> <script> import axios from 'axios' import { mapActions } from 'vuex' export default { name: 'Home', methods: { ...mapActions(['addToCart']), async getProducts() { const response = await axios.get('/api/products') this.products = response.data } }, data() { return { products: [] } }, mounted() { this.getProducts() } } </script> ``` 8. 在Cart.vue组件中显示购物车: 在Cart.vue组件中,使用Vuex store中的数据显示已添加到购物车的产品,并允许用户更改数量或删除产品: ```javascript <template> <div class="container"> <div class="row"> <div class="col-sm-8"> <div class="card mb-3" v-for="item in cart" :key="item.id"> <div class="card-body"> <h5 class="card-title">{{ item.name }}</h5> <p class="card-text">{{ item.price * item.quantity }}</p> <div class="input-group mb-3"> <div class="input-group-prepend"> <button class="btn btn-outline-secondary" type="button" @click="decrementQuantity(item)">-</button> </div> <input type="text" class="form-control" :value="item.quantity" @input="updateQuantity(item, $event.target.value)"> <div class="input-group-append"> <button class="btn btn-outline-secondary" type="button" @click="incrementQuantity(item)">+</button> </div> </div> <button class="btn btn-danger" @click="removeFromCart(item)">Remove</button> </div> </div> </div> <div class="col-sm-4"> <div class="card"> <div class="card-body"> <h5 class="card-title">Cart Summary</h5> <p class="card-text">Total items: {{ cartItemCount }}</p> <p class="card-text">Total price: {{ cartTotal }}</p> <router-link to="/" class="btn btn-primary">Continue Shopping</router-link> </div> </div> </div> </div> </div> </template> <script> import { mapState, mapActions } from 'vuex' export default { name: 'Cart', computed: { ...mapState(['cartTotal', 'cartItemCount']), cart() { return this.$store.state.cart } }, methods: { ...mapActions(['removeFromCart', 'updateQuantity']), incrementQuantity(product) { this.updateQuantity({product, quantity: product.quantity + 1}) }, decrementQuantity(product) { if (product.quantity > 1) { this.updateQuantity({product, quantity: product.quantity - 1}) } } } } </script> ``` 后端: 1. 创建Node.js项目: 在命令行中,输入以下命令创建一个新的Node.js项目: ``` mkdir shopping-cart-backend cd shopping-cart-backend npm init -y ``` 2. 安装必要的依赖项: ``` npm install express cors ``` 3. 创建API服务: 在index.js文件中,使用以下代码创建API服务: ```javascript const express = require('express') const cors = require('cors') const app = express() app.use(cors()) const products = [ { id: 1, name: 'Product 1', price: 10 }, { id: 2, name: 'Product 2', price: 20 }, { id: 3, name: 'Product 3', price: 30 }, { id: 4, name: 'Product 4', price: 40 } ] app.get('/api/products', (req, res) => { res.json(products) }) app.listen(3000, () => { console.log('Server listening on port 3000') }) ``` 4. 启动API服务: 在命令行中,输入以下命令启动API服务: ``` node index.js ``` 现在,您可以在浏览器中访问http://localhost:3000/api/products,以获取产品列表。 完成以上步骤后,您可以使用以下命令启动Vue.js应用程序: ``` npm run serve ``` 然后,您可以在浏览器中访问http://localhost:8080,以查看并测试购物车应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值