【day 12】插槽和vue3

这篇具有很好参考价值的文章主要介绍了【day 12】插槽和vue3。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

插槽

用于父组件给子组件内 传递html内容

默认插槽

app.vue

<child>
      <ul>
        <li v-for="item in list" :key="item.id">{{ item.name }}</li>
      </ul>
    </child>
 
 child.vue
 
  <div class="child">
    我是子组件
    <hr />
    <!-- 将会接收 父组件内 child标签中间的dom结构 -->
    <slot></slot> 
  </div>
 

具名插槽

<child>
      <template slot="slotA">
        <img src="@/assets/logo.png" alt="" />
      </template>

      <template slot="slotB">
        <ul>
          <li v-for="item in list" :key="item.id">{{ item.name }}</li>
        </ul>
      </template>
    </child>
    
    
    
     <div class="child">
    <slot name="slotA"></slot>
    <hr />
    我是子组件
    <hr />
    <!-- 将会接收 父组件内 child标签中间的dom结构 -->
    <slot name="slotB"></slot>
  </div>

作用域插槽

适用于: 数据不在父组件内 在 子组件内 如果父组件想个性化定制子组件的内容

<div class="child">
    我是子组件
    <hr />
    <!-- 将会接收 父组件内 child标签中间的dom结构 -->
    <slot :listData="list"></slot>
  </div>
list在child内 
data() {
    return {
      list: [
        { id: 0, name: "zhuque" },
        { id: 1, name: "wanzi" },
        { id: 2, name: "yingtao" },
        { id: 3, name: "afei" },
      ],
    };



父组件内 

<child>
      <template scope="abc">
        <!-- abc会接收来自于child组件内 slot标签的 所有的属性值 { "listData": [ { "id": 0, "name": "zhuque" }, { "id": 1, "name": "wanzi" }, { "id": 2, "name": "yingtao" }, { "id": 3, "name": "afei" } ] }-->
        <!-- {{ abc }} -->
        <ul>
          <li v-for="item in abc.listData" :key="item.id">{{ item.name }}</li>
        </ul>
      </template>
    </child>

    <child>
      <template scope="data">
        <ol>
          <li v-for="item in data.listData" :key="item.id">{{ item.name }}</li>
        </ol>
      </template>
    </child>





通过 解构赋值 

<child>
      <template scope="{listData}">
        <ul>
          <li v-for="item in listData" :key="item.id">{{ item.name }}</li>
        </ul>
      </template>
    </child>


普通节点 不能直接用scope  得用slot-scope
<child>
      <ol slot-scope="{ listData }">
        <li v-for="item in listData" :key="item.id">{{ item.name }}</li>
      </ol>
    </child>

Vue3

确保 你的 vue-cli 是 4.5.0的版本以上

vue --verstion的方式查看 版本过低请更新版本

初始文件对比
// vue2

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

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

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



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

const app = createApp(App)
//比之前的 vm 身上要少一些东西  创建实例对象 

app.use(store).use(router).mount('#app')
router/index.js的区别
vue2

import Vue from 'vue'
import VueRouter from 'vue-router'


Vue.use(VueRouter)

const routes = [
]
// history模式  hash模式 
const router = new VueRouter({
  mode: 'history',
  routes
})

export default router

import { createRouter, createWebHistory } from 'vue-router'

import Home from '../views/Home.vue'

const routes = [
]

// createRouter 创建 router实例对象 
const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router


修改 路由模式 
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
  history: createWebHashHistory(),
  routes
})
store/index.js
vue2
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})
vue3
import { createStore } from 'vuex'

// createStore 创建 store实例对象 

export default createStore({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

关于 版本
vue2 
"vue-router": "^3.2.0",
    "vuex": "^3.4.0"
    不能使用 4版本 
    
 vue3内 
 "vue-router": "^4.0.0-0",
  "vuex": "^4.0.0-0"
    不能使用 3版本 

初始 setup

vue3一个新的配置项 一个函数

vue2中 option配置项 data methods computed 等等 都会换个形式 写入 setup函数内

data中的数据 

data() {
    return {
      msg: "你好",
    };
  },
  
setup函数内
 setup() {
    let msg = "你好";

    return {
      msg,//暴露给模板使用 不return 模板没法使用 msg数据  
    };
  },

setup函数返回值

1.返回数据给模板使用 (返回一个对象  常用)

2.(很少用) return 渲染函数 

<template>
  <div id="nav">我是 App组件{{ msg }}</div> //h1就会出现在这 
</template>
<script>
import { h } from "vue";
export default {
  name: "App",
  setup() {
    return () => h("h1", "我是h1标签");
  },
};
</script>
<style></style>

setup中的 this

undefined  不指向组件实例   
原因: setup执行的时机  beforeCreate之前  

所以在setup函数内 几乎不使用 this 
也意味着 最好是不要和 option中的 data method 等配置 混用 

method配置项 直接在 setup内定义函数即可

<template>
  <div id="nav">我是 App组件{{ msg }}</div>
  <button @click="changeMsg">修改 msg</button>
</template>

setup() {
    let msg = "你好";

    function changeMsg() {
      // 修改msg
      console.log(msg);
    }
    return {
      msg,
      changeMsg,// !一定要 return出去 要不然模板拿不到 
    };
  },

数据响应式问题

当我们触发 btn时发现  msg已经发生了变化  但是视图没有更新  数据不具有响应式 

<div id="nav">{{ msg }}</div>
  <button @click="changeMsg">修改 msg</button>

setup() {
    let msg = "你好";
    function changeMsg() {
      // 修改msg
      msg += "!!!!";
      console.log(msg);
    }
    return {
      msg,
      changeMsg,
    };
  },

ref 函数

//要用 必须导入 
import { ref } from 'vue';

let msg = ref("你好"); 

ref 作用 : 定义一个响应式数据  
let /const xxx = ref(数据源)

msg ==> RefImpl{}   包含了响应式数据的引用对象 // 简称ref对象 
RefImpl{
    value:(...) //对象代理 
}

数据 存在 msg.value属性上了 

<div id="nav">{{ msg }}</div>
<button @click="changeMsg">修改 msg</button>
setup() {
    let msg = ref("你好");
    console.log(msg);

    function changeMsg() {
      msg.value += "!!!!";
      console.log(msg);
    }
    return {
      msg,
      changeMsg,
    };
  },
 let msg = '你好'
1.如果想要数据具有响应式  ref函数包裹为RefImpl{}对象 let msg = ref('你好')
2.修改ref数据  msg.value 去修改  
3.return的时候 直接return msg 
4.模板里使用时  直接使用 msg 


注意: 
function changeMsg() {
      // msg.value += "!!!!";
      msg = "你好你好"; //切断了 msg 和 RefImpl{}的引用关系  会失去响应式 
      console.log(msg);
    }


对对象类型的数据
能响应式监听到 对象属性的增删 (vue2不能做到的)
不需要再使用Vue.set vm.$set对对象进行操作了 

对于数组的 下标操作 也可以响应式监听到变化了 (但是vue2就不行 ) 

<template>
  <div id="nav">{{ msg }}</div>
  <p>名字: {{ person.name }}</p>
  <p>年龄: {{ person.age }}</p>
  <p>爱好: {{ person.hobby }}</p>
  <ul>
    <li v-for="item in arr">{{ item }}</li>
  </ul>
  <button @click="changeMsg">修改 msg</button>
  <button @click="changeAge">修改 person.age</button>
  <button @click="changeArr">修改 arr</button>
</template>
<script>
import { ref } from "vue";
export default {
  name: "App",
  setup() {
    let msg = ref("你好");
    let person = ref({
      name: "zhuque",
      age: 3,
    });

    let arr = ref(["我", "爱"]);
    console.log(person);

    function changeMsg() {
      msg.value = "你好你好";
      console.log(msg);
    }
    function changeAge() {
      person.value.age++;
      person.value.hobby = "打球";
      delete person.value.name;
    }

    function changeArr() {
      arr.value[2] = "你"; //对于数组的 下标操作 也可以响应式监听到变化了 (但是vue2就不行 ) 
    }

    return {
      msg,
      changeMsg,
      person,
      changeAge,
      arr,
      changeArr,
    };
  },
};
</script>
<style></style>

App.vue

<template>
 <div id="nav">我是App组件{{ msg}}</div>
 <p>
  名字:{{ person.name }}
  年龄:{{ person.age }}
  爱好:{{ person.hobby }}
 </p>
 <ul>
  <li v-for="item in arr" :key="item"> {{item}}</li>
 </ul>
 <button @click="changeMsg">修改msg</button>
 <button @click="changeAge">修改年龄</button>
 <button @click="changeArr">修改arr</button>
</template>
<script>
import { ref }  from "vue"
   export default{
    name:"App",
    // data(){
    //   return{
    //     msg:"你好",
    //   };
    // },

 setup(){
    let msg = ref("你好");
    let person = ref({
      name:"zz",
      age:4
    });
    let  arr = ref(['我','喜','欢','你']) 
    console.log(this) //undefined
    function changeMsg(){
      // 修改msg
      msg.value+="!!!!"
      console.log(msg)
    }
    function changeAge(){
      // person.value.age++
      // person.value.hobby="打球"
      delete person.value.name
    }
    function changeArr(){
         arr.value[4]="呀"
    }

    return{
      msg,
      changeMsg,
      person,
      changeAge,
      arr,
      changeArr
    }
 }

   };

</script>
<style lang="scss">

</style>

ref处理对象

无论对象内嵌套多少对象 都是做了响应式处理的

ref 中 定义基础数据类型 通过 defineProperty实现的

ref中 定义对象数据类型 底层借助了 reactive 函数 把对象 包装为proxy代理对象

<template>

  <p>名字: {{ person.name }}</p>
  <p>年龄: {{ person.age }}</p>
  <p>爱好: {{ person.hobby }}</p>
  <ul>
    <li v-for="item in person.tags" :key="item">{{ item.name }}</li>
  </ul>

  <button @click="changeTags">修改 person.tags[0]</button>

</template>
<script>
import { ref } from "vue";
export default {

  name: "App",
  setup() {
    let msg = ref("你好");
    let person = ref({
      name: "zhuque",
      age: 3,
      tags: [{ name: "美女" }, { name: "富婆" }],
    });

    function changeTags() {
      person.value.tags[0].name = "小可爱";
    }

    return {
      person,
      changeTags,
    };
  },
};
</script>
<style></style>

setup函数内的 props参数

props的接收
app.vue 
<child :data="msg"></child>

child.vue
<template>
  <div class="child">
    <h1>子组件:{{ data }}</h1>
  </div>
</template>
<script>
export default {
  name: "Child",
  props: ["data"], //如果没有接收  setup函数内的props会是空对象  
  setup(props) {
    console.log(props); // Proxy {data: '你好'}
  },
};
</script>


setup函数内的 context参数

1. // context.attrs  等同于 之前 vue2 this.$attrs
// context.emit  等同于 之前的 vue2 this.$emit 

child 
emits: ["change"], // 写一下更好
setup(props, context) {
    // console.log(props.data);
    console.log(context);
    // context.attrs  等同于 之前 vue2 this.$attrs

    //change 触发 Child组件上的自定义 change事件
    context.emit("change");
  }
      
app 
<child :data="msg" @change="fn"></child>
function fn() {
    msg.value = "你好你好啊~~~~~";
    console.log("触发了 change自定义事件");
}


由于 emit用的比较多 一般解构出来用 
setup(props, { emit }) {
    let childData = ref("子组件数据");
    let num = ref(0);
    //change 触发 Child组件上的自定义 change事件
    emit("change");
    return {
      childData,
      num,
    };
  },

vue3中 ref的变化

vue2 
<child ref="childRef"></child>
获取的时候  this.$refs.childRef 去获取 child实例对象 




vue3中 
  setup() {
    const childRef = ref(null); //第一步
      
	return{
        childRef//第二步
    }
  }
第三步 : <child ref="childRef"></child>
在setup中 操作 childRef 
function getChild() {
      console.log(childRef.value); //直接访问 .value 即可 
    }


   
expose参数
vue2中 利用ref获取 子组件内的data数据 
<child :data="msg" @change="fn" ref="childRef"></child>
methods: {
    getChild() {
      console.log(this.$refs.childRef); //能获取到 child组件的 vc对象 data上的所有的数据 
    },
  }
  
child.vue
  data() {
  return {
  childData1: "11111",
  };
  },
      
      
假设我们不想 暴露vc给别的组件  只希望暴露指定的数据  

context.expose({
      childData, // 通过ref 不再能获取到 组件的vc对象 而是只能获取 childData数据 
    });
    return {
      childData,
      num,
    };

reactive函数

作用: 定义一个对象类型的响应式数据 (基础数据类型 不能用它 得用ref 函数)

  1. const 代理对象 = reactive(源对象) 接收一个对象 或者 数组
  2. 定义的响应式数据 是’深层次’ 内部的数据都是响应式的
  3. 基于 es6的 proxy 实现的
 let person = reactive({ //不能放基础数据类型  
      name: "zhuque",
      age: 3,
      tags: [{ name: "美女" }, { name: "富婆" }],
    });

function changeAge() {
      // person.age++;
      // person.hobby = "打球";
      person.tags[0].name = "小可爱"; 
    }

和 ref 对比一下

在定义上  ref() //可以接收任意数据类型 
		reactive //只接受 对象类型
        
在setup内操作时  
ref定义的数据  
	xxx.value.msg 
reactive定义的数据 
	xxx.msg 

return的时候 都是直接 return 
模板内使用时 也是直接使用  

vue3新版本中 建议 使用 ref

computed

<input type="text" v-model="firstname" />
<input type="text" v-model="lastname" />
<p>全名: {{ fullname }}</p>


import { ref, computed } from "vue";
setup() {
    let firstname = ref("王");
    let lastname = ref("大锤");

    // computed
    // 计算属性 依赖于其他的响应式数据 数据发生变化 计算属性会重新计算
    let fullname = computed(() => {
      //只有getter
      return firstname.value + "-" + lastname.value;
    }); 

    return {
      firstname,
      lastname,
      fullname,
    };
  },
  
  如果 计算属性 需要被修改 
  对象写法 
  
   <input type="text" v-model="firstname" />
    <input type="text" v-model="lastname" />
    <p>全名: {{ fullname }}</p>
    <input type="text" v-model="fullname" />
    
  let fullname = computed({
      get() {
        //getter
        return firstname.value + "-" + lastname.value;
      },
      set(newValue) {
        firstname.value = newValue.split("-")[0];
        lastname.value = newValue.split("-")[1];
      },
    });
    
    
    //在setup中 使用 计算属性时 
    
    fullname //ComputedRefImpl{}
    
    console.log(fullname.value);
    
computed多个情况 (了解)
 当计算属性 添加到 一个对象内 这个对象是响应式时  person.value.fullname 就是 本身的值  
 <div id="nav">
    <input type="text" v-model="person.firstname" />
    <input type="text" v-model="person.lastname" />

    <p>全名: {{ person.fullname }}</p>
    <input type="text" v-model="person.fullname" />
  </div>
setup() {
    let person = ref({
      firstname: "王",
      lastname: "大锤",
    });

    // computed
    // 计算属性 依赖于其他的响应式数据 数据发生变化 计算属性会重新计算
    person.value.fullname = computed(() => {
      return person.value.firstname + "-" + person.value.lastname;
    });
    console.log(person.value.fullname); // 王-大锤 而不是 ComputedRefImpl对象 
    
    
    
    //当计算属性 添加到 一个对象内 这个对象是非响应式数据时  person.fullname 就会被处理为 ComputedRefImpl对象  
     let person2 = {};
     
     person2.fullname = computed(() => {
      return person.value.firstname + "-" + person.value.lastname;
    });
    console.log(person2.fullname); //ComputedRefImpl对象 

! 如果不知道 啥时候该.value 用前 打印看看样子

watch

<h1>msg: {{ msg }}</h1>
<input type="text" v-model="person.firstname" />
<input type="text" v-model="person.lastname" />
<p>全名: {{ fullname }}</p>

<button @click="msg += '!'">修改 msg</button>

// 监听ref基础值类型  (切记不能监听msg.value 监听字符串无意义)
    watch(msg, (newValue, oldValue) => {
      console.log(oldValue, newValue);
    });

 //监听 ref多个数据 
    watch([msg, num], (newValue, oldValue) => {
      console.log(oldValue, newValue); //['你好', 2] (2) ['你好!', 2]
    });

//监听 ref对象类型  得.value 获取不到旧值 深度监听 (默认开启)(当person内的任意属性发生变化 都能触发 watch回调运行 )
    watch(person.value, (newValue, oldValue) => {
      console.log(oldValue, newValue);
    });

 //监听 ref 数组类型 得.value 获取不到旧值
    watch(arr.value, (newValue, oldValue) => {
      console.log(oldValue, newValue);
    });

// 监听 reactive 对象类型 不需要.value   获取不到旧值
    watch(person, (newValue, oldValue) => {
      console.log(oldValue, newValue);
    });

//只监听 person内某个属性  监听一个基础值 会报错 
    watch(person.value.firstname, (newValue, oldValue) => {
      console.log(oldValue, newValue);
    });
以上的解决方案
//只监听 person内某个属性 箭头包一下 return 
    watch(
      () => person.value.firstname,
      (newValue, oldValue) => {
        console.log(oldValue, newValue);
      }
    );

// 只监听 person内某个属性  监听一个对象 这样写 ok 
    watch(person.value.tags, (newValue, oldValue) => {
      console.log(oldValue, newValue);
    });

//一些其他的参数 
watch(
      person.value.tags,
      (newValue, oldValue) => {
        console.log(oldValue, newValue);
      },
      {
        immediate: true, 
      }
    );

watch

<h1>msg: {{ msg }}</h1>
<input type="text" v-model="person.firstname" />
<input type="text" v-model="person.lastname" />
<p>全名: {{ fullname }}</p>

<button @click="msg += '!'">修改 msg</button>

// 监听ref基础值类型  (切记不能监听msg.value 监听字符串无意义)
    watch(msg, (newValue, oldValue) => {
      console.log(oldValue, newValue);
    });

 //监听 ref多个数据 
    watch([msg, num], (newValue, oldValue) => {
      console.log(oldValue, newValue); //['你好', 2] (2) ['你好!', 2]
    });

//监听 ref对象类型  得.value 获取不到旧值 深度监听 (默认开启)(当person内的任意属性发生变化 都能触发 watch回调运行 )
    watch(person.value, (newValue, oldValue) => {
      console.log(oldValue, newValue);
    });

 //监听 ref 数组类型 得.value 获取不到旧值
    watch(arr.value, (newValue, oldValue) => {
      console.log(oldValue, newValue);
    });

// 监听 reactive 对象类型 不需要.value   获取不到旧值
    watch(person, (newValue, oldValue) => {
      console.log(oldValue, newValue);
    });

//只监听 person内某个属性  监听一个基础值 会报错 
    watch(person.value.firstname, (newValue, oldValue) => {
      console.log(oldValue, newValue);
    });
以上的解决方案
//只监听 person内某个属性 箭头包一下 return 
    watch(
      () => person.value.firstname,
      (newValue, oldValue) => {
        console.log(oldValue, newValue);
      }
    );

// 只监听 person内某个属性  监听一个对象 这样写 ok 
    watch(person.value.tags, (newValue, oldValue) => {
      console.log(oldValue, newValue);
    });
//另一个写法
watch(
      () => person.value.tags,
      (newValue, oldValue) => {
        console.log(oldValue, newValue);
      },
      {
        deep: true, //如果() => person.value.tags, 开启深度监视
      }
    );

//一些其他的参数 
watch(
      person.value.tags,
      (newValue, oldValue) => {
        console.log(oldValue, newValue);
      },
      {
        immediate: true, 
      }
    );


如果需要 获取旧的值 
import _ from "lodash"; //不需要npm 
watch(
      () => _.cloneDeep(person.value.tags), //深拷贝对象 
      (newValue, oldValue) => {
        console.log(oldValue, newValue);
      }
    );

watchEffect

响应式数据自动应用使用 watchEffect 函数 参数是一个回调函数 对函数内的 响应式的数据 进行追踪 数据发生了变化 就会触发运行该函数

import { watchEffect } from "vue";

 watchEffect(() => {
     //query.value 发生变化 就会触发watchEffect回调函数重新运行   
      let queryValue = query.value; //变化的量
      console.log("watchEffect函数执行了 发起请求 请求api(queryValue)");
    }); // 不管依赖的数据 发不发生变化  默认先执行一次

允许停止 监听 

// 允许停止监听
const wName = watchEffect(() => {
    let queryValue = query.value; //变化的量
    console.log("watchEffect函数执行了 发起请求 请求api(queryValue)");
});

wName(); 停止 监听  

生命周期

(vue2)option内api    setup 内    
beforeCreate         不存在 相当于setup() 
created              不存在 相当于setup() 
beforeMount          onBeforeMount
mounted              onMounted  (请求数据 初始化的操作)
beforeUpdate         onBeforeUpdate  
updated              onUpdated 
beforeDestroy        onBeforeUnmount 销毁前执行 
destroyed            onUnmounted   销毁后执行 
activated            onActivated 组件激活时调用
deactivated          onDeactivated 组件失活时调用 
import { ref, onBeforeMount } from "vue"; //先导入 再使用 除了destroyed 其他就是 vue2基础上 加个  on  

setup(){
onBeforeMount(() => {
      console.log("onBeforeMount");
    });
}

自定义hook

changeColor.js
import { ref } from 'vue'
// 导出一个函数

export const changeColor = () => {
  const color = ref("red");

  const changeColorApi = () => {
    color.value = randomColor();
  };
  const randomColor = () => {
    return `rgb(
      ${randomNum(0, 255)},
      ${randomNum(0, 255)},
      ${randomNum(0, 255)})`;
  };
  const randomNum = (num1, num2) => {
    return Math.floor(Math.random() * (num2 + 1 - num1) + num1);
  };

  return {
    //希望给app.vue用到的数据 
    changeColorApi,
    color
  }
}
app.vue内

<template>
  <div id="nav">
  
    <div class="box" :style="{ background: color }">切换颜色</div>
    <button @click="changeColorApi">修改颜色</button>
  </div>
</template>
<script>
import { onBeforeMount, onMounted, ref } from "vue";
import { changeColor } from "./changeColor.js";
export default {
  name: "App",
  setup() {

    // hook 本质就是一个函数 单独封装了一些功能
    const { color, changeColorApi } = changeColor();

    return {
      color,
      changeColorApi,
    };
  },
};
</script>
<style></style>

1. 抽离功能 形成js文件  导出一个函数  需要暴露数据给 组件用 return数据
2. 组件要使用 导入 js文件 导入函数 
3. 运行函数  得到返回值 

好处: 结构清晰好维护  代码可复用

toRef

作用 : 用来为源响应式对象上的某个属性新创建一个ref。然后ref可以被传递,一直保持对其源属性响应式连接在一起

语法  const fullname = toRef(person,'fullname')

应用 : 如果希望 讲响应式对象中的某个属性 单独提供给外部使用时 
      如果该属性是 基础数据类型 与原属性丢失响应式 (fullname 和 person没有关系了 person发生变化 外部的 fullname不会发生变化 )
错误示范 : 
let person = ref({
      firstname: "王",
      lastname: "大锤",
      fullname: "王大锤",
      tags: [{ name: "跳舞" }],
    });
 return {
      person,
      fullname:person.value.fullname, //丢失响应式连接   person发生变化 外部的 fullname不会发生变化 
      changeFullname,
    };

正确操作 :  能保持和源属性响应式连接 
 const changeFullname = () => {
      person.value.fullname = "王大大大大";
    };
    const fullname = toRef(person.value, "fullname");
    console.log(fullname); // ObjectRefImpl
return {
      person,
      fullname,
      changeFullname,
    };

多个属性需要 在模板内直接使用

 const fullname = toRef(person.value, "fullname");
    const firstname = toRef(person.value, "firstname");
    const lastname = toRef(person.value, "lastname");
    return {
      fullname,
      changeFullname,
      firstname,
      lastname,
    };

toRefs

const personObj = toRefs(person.value);
 return {
      changeFullname,
      ...personObj,
    };

shallowReactive 与 shallowRef

shallowReactive : 只处理对象最外层属性的响应式

shallowRef 只处理 基础数据类型的响应式 不进行对象的响应式处理

用途:

如果有一个对象 结构比较深 变化只有外层属性变化 ==> shallowReactive

如果有一个对象 后续功能不会修改对象内的属性 而是生成新的对象来替换 shallowRef

let person = shallowReactive({
      firstname: "王",
      lastname: "大锤",
      fullname: "王大锤",
      tags: [{ name: "跳舞" }],
    });
    let arr = ref([]);
    const changeTags = () => {
      person.tags[0].name = "唱歌"; //没法监听到变化  
    };

    const changeFullname = () => {
      person.fullname = "王大大大大"; //可以监听到变化 
    };




let person = shallowRef({ //不响应对象 
      firstname: "王",
      lastname: "大锤",
      fullname: "王大锤",
      tags: [{ name: "跳舞" }],
    });
const changeFullname = () => {
      // person.value.fullname = "王大大大大"; //没有变化 
      person.value = { //有变化 
        firstname: "王~~~~",
        lastname: "大锤~~~~~",
        fullname: "王大锤~~~~",
        tags: [{ name: "跳舞~~~~~" }],
      };
    };


应用 : 动态组件中

<component :is="data.componentId"></component>


const bool = ref(true);

    let data = shallowRef({ //使用ref 会得到警告 对组件响应式化浪费性能
      componentId: bool.value ? Child : HelloWorld,
    });
    
    return {
    data, 
    }

readonly 与shallowReadonly

  • readonly 让一个数据(或者响应式数据) 变为只读的 (深只读 只能获取不允许修改)
  • shallowReadonly让一个数据(或者响应式数据) 变为只读的 (浅只读 内层的属性对象可以修改)
  • 不希望你的数据被修改时 ==> 第三方的插件
import { computed, readonly, ref, shallowRef } from "vue";
let person = readonly({ // proxy 不需要.value
      firstname: "王",
      lastname: "大锤",
      fullname: "王大锤",
      tags: [{ name: "跳舞" }], 
    });

操作person内的属性 会报警告 

 
let person = shallowReadonly({ // proxy 不需要.value
      firstname: "王",
      lastname: "大锤",
      fullname: "王大锤",//修改fullname 报警告
      tags: [{ name: "跳舞" }], //修改tags内的数据 会引发变化 但是 视图不更新 
    });

toRaw 与 markRaw

  • toRaw :

    作用: 将一个 由 reactive 生成的响应式对象 转为 普通对象

    使用场景 : 用于读取响应式对象对应的普通对象 对这个普通对象的所有操作 不会引发页面更新

let person = reactive({
      firstname: "王",
      lastname: "大锤",
      fullname: "王大锤",
      tags: [{ name: "跳舞" }],
    });
    const personRaw = toRaw(person); 
    console.log(personRaw)
  • markRaw:
  • 作用: 标记一个对象 使其永远不会成为响应式对象
  • 应用场景 有些值 不应该被设置为响应式的 例如复杂的第三方类库
  • 当渲染具有不可变数据源的大列表时 , 跳过响应式转化 可以提高性能
let person = markRaw({
      firstname: "王",
      lastname: "大锤",
      fullname: "王大锤",
      tags: [{ name: "跳舞" }],
    });

    console.log(person);//普通对象 
    console.log(reactive(person)); //还是普通对象  

customRef

作用: 自定义ref

<input type="text" v-model="query" />
<p>{{ query }}</p>    

const useDebouncedRef = (value) => {
      // 返回一个特定的 ref对象
      return customRef((track, trigger) => {
        // track 进行追踪数据变化
        return {
          //getter 和 setter
          get() {
            console.log("执行");
            track(); //value获取到最新的依赖值
            return value;
          },
          set(newValue) {
            console.log("设置");
            value = newValue;
            setTimeout(trigger, 1000);
            // trigger(); //触发get函数重新运行 触发Vue去更新界面
          },
        };
      });
    };
    const query = useDebouncedRef("");
    console.log(query); //CustomRefImpl
实现防抖效果

用户瞬间的操作 都会导致事件高频触发 如果响应复杂 高频触发 可能会导致响应更不上 页面卡顿 假死现象

实时检查输入 绑定了 keyup事件 请求服务端响应搜索结果 触发频率太高 导致大量的请求发出 响应速度更不上

解决方案: 限制一下 事件的触发频率

经典方案: 设定一个周期延迟的执行动作 如果期间事件又被触发 重新设定周期 知道周期结束我们才执行动作

     <input type="text" v-model="query" />
    <p>{{ query }}</p>
 
 const useDebouncedRef = (value) => {
      // 返回一个特定的 ref对象
      return customRef((track, trigger) => {
        // track 进行追踪数据变化
        let timer = null;
        return {
          //getter 和 setter
          get() {
            track(); //value获取到最新的依赖值
            return value;
          },
          // 1 500ms之后 2 发送两个请求
          // 1 马上按 2  发送一个请求
          set(newValue) {
            clearTimeout(timer);
            timer = setTimeout(() => {
              console.log("发送请求");
              value = newValue;
              trigger(); //触发get函数重新运行 触发Vue去更新界面
            }, 500);
          },
        };
      });
    };
    const query = useDebouncedRef("");
    console.log(query); //CustomRefImpl 

响应式数据的判断

返回布尔值

  • isRef: 检查一个值是否为一个 ref 对象
  • isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
  • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
  • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理

祖 ==> 孙组件之间的通信

provide与inject

用之前的props也能实现 但是要先爷爷传给儿子 儿子再传给孙子 很麻烦 
祖组件中 : 传递 
setup() {
    const msg = ref("我是爷爷给孙子的数据");
    //要传递一个 msgData 里面装着 ref("我是爷爷给孙子的数据")这个数据
    provide("msgData", msg);
  },

孙组件中 : 接收
setup() {
    const msgFromGrandFather = inject("msgData");
    return {
      msgFromGrandFather,
    };
  },

父传子 : props

子传父: 自定义事件

任意组件通信 全局事件总线 vuex

祖传孙 : provide与inject

其他的新增和修改

Fragment

vue3组件中 template 并不是只允许一个根节点

原因: Fragment 内部会将多个标签 包含在一个 Fragment 虚拟元素中

好处: 减少标签层级 减小内存的占用

Teleport 标签

是一个能够将我们组件html结构 移动到指定位置的技术 ( 为了更好的写css 尤其有定位时 )

注意:在安装组件之前,目标元素必须存在-即,目标不能由组件本身呈现,理想情况下应该位于整个Vue组件树之外 ( 要么body 要么 #app )

<teleport to="body"> //节点会移动到 body的位置  
    <div class="sun">
      <h1>我是孙子 {{ msgFromGrandFather }}</h1>
    </div>
  </teleport> 
  
  .sun {
  position: absolute; 
  left: 50%;
  top: 50%;
  width: 200px;
  height: 200px;
  transform: translate(-50%, -50%);
  background-color: blue;
}

修改

vue2.x 

注册全局组件 
Vue.component('组件名',组件对象)

注册全局指令 
Vue.directive('指令名',指令对象)

Vue.mixin()

Vue.use()
vue 3.x
注册全局组件 
app.component('组件名',组件对象)
注册全局指令
app.directive('指令名',指令对象)

app.mixin()

app.use()
vue2 中               vue3中
Vue.prototype         app.config.globalProperties  //Vue原型上添加属性 
Vue.config.xxx        app.config.xxxx

过渡类名的写法

vue2 

v-enter,v-leave-to{}
v-leave,v-enter-to{}
vue3
在起点项中 加上 -from
v-enter-from,v-leave-to{}
v-leave-from,v-enter-to{}

css操作

绑定数据

以v-bind css函数 把css的值关联到组件状态文章来源地址https://www.toymoban.com/news/detail-515757.html

setup() {
    const fontColor = ref("red");

    return {
      fontColor,
    };
  },
      
<style>
.grand-father {
  color: v-bind(fontColor); 
}
</style>


深度操作
提供了修改第三方Ui框架组件样式的 方案 

<Child></Child>
app 想要 控制 child组件内的 h1的css

.child :deep(h1) {
  background-color: yellow;
}
全局选择器
在组件内 修改 body html的样式 是不成功的 

:global(body) {
  background-color: red;
}

Vuex 的变化

使用 state内的数据 
import { useStore } from "vuex";
const store = useStore();
const msg = computed(() => store.state.msg);

getters 内的同理  
const msgGetter = computed(() => store.getters.msg); //保留响应性 computed操作一下   

store.commit("FN"); // 触发mutation内的函数 
store.dispatch("fn"); // 触发action内的函数

不用 map...系列了    直接用store对象操作一切 

Router的变化

vue2中 

this.$router.push() 进行路由跳转 
this.$route获取路由信息

vue3
import { useRouter, useRoute } from "vue-router";
setup() {
    const router = useRouter();
    const route = useRoute();

    // router.push('/')
    console.log(route);
  },

缓存路由组件写法修改

vue2中 
<keep-alive>
	<router-view></router-view>
</keep-alive>

升级为下面的写法
vue3中
    
<router-view v-slot="{ Component }"> slot标签传给 父组件的 数据 
      <keep-alive>
        <component :is="Component" />
      </keep-alive>
</router-view>

插槽

slot='{}' 

v-slot vue3新增的指令  

实现作用域插槽
 <Sun>
      <template v-slot="{ msg }">
        <h2>{{ msg }}</h2>
        <img src="@/assets/logo.png" alt="" />
      </template>
    </Sun>
    
<div class="sun">
    <h1>我是孙子</h1>
    <slot :msg="msg"></slot>
  </div>


实现作用域以及 具名插槽

 <Sun>
      <template v-slot:slotA="{ msg }">
        <h2>{{ msg }}</h2>
        <img src="@/assets/logo.png" alt="" />
      </template>
      <template v-slot:slotB="{ msg1 }">
        <h2>{{ msg1 }}</h2>
        <img src="@/assets/logo.png" alt="" />
      </template>
    </Sun>


<slot :msg="msg" name="slotA"></slot>
<slot :msg1="msg1" name="slotB"></slot>

v-model 使用到 自定义组件上

语法糖的处理

child.vue 

<template>
  <div class="child">
    <h1>我是儿子</h1>
    <Sun v-if="bool" v-model="bool"></Sun>
    <button @click="fn">点击展示 Sun组件</button> 
  </div>
</template>
<script>
import { ref } from "vue";
import Sun from "./Sun.vue";
export default {
  name: "Child",
  components: { Sun },
  setup() {
    const bool = ref(false);
    const fn = () => {
      //显示 Sun组件
      bool.value = true;
    };
    return {
      bool,
      fn,
    };
  },
};
</script>



Sun.vue中
<template>
  <div class="sun">
    <h1>我是孙子</h1>
    <button @click="closeFn">关闭按钮</button>
  </div>
</template>
<script>
import { inject, ref } from "vue";
export default {
  name: "Sun",
  props: ["modelValue"],
  setup(props, { emit }) {
    const closeFn = () => {
      emit("update:modelValue", false);
    };

    return {
      closeFn,
    };
  },
};
</script>
<style scoped></style>



原理

 <Sun
      v-if="bool"
      :modelValue="bool"
      @update:modelValue="modelValue = $event"
    ></Sun>

script setup写法

defineProps

<Child :data="num"></Child>

//在这里定义的数据 函数 props可以直接在模板内使用 不需要 return 
<script setup>
import { ref } from "vue";
import Sun from "./Sun.vue";

const props = defineProps(['data']) //里面接收数组写法 和 对象写法 
console.log(props.data);

const bool = ref(false);
const fn = () => {
  //显示 Sun组件
  bool.value = true;
};
</script>

导入子组件

只需要导入 就可以直接使用
<Sun
      v-if="bool"
      v-model="bool"
      :modelValue="bool"
      @update:modelValue="modelValue = $event"
    ></Sun>
import Sun from "./Sun.vue";

defineEmits

const emit = defineEmits(["select"]);

const bool = ref(false);
const fn = () => {
  //显示 Sun组件
  emit("select");
  bool.value = true;
};

父组件中 
<Child :data="num" @select="fn"></Child>
const fn = () => {
  console.log("App.vue");
};

defineExpose

跟之前不同的点是   <script setup> 默认关闭的  (之前的写法 可以通过 ref获取组件的实例对象 然后获取他的数据)
defineExpose({
  bool, // 外部只能获取到 bool数据 
});

到了这里,关于【day 12】插槽和vue3的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端(四)——vue.js、vue、vue2、vue3

    😊博主:小猫娃来啦 😊文章核心: vue.js、vue、vue2、vue3从全局到局部 Vue.js是一款流行的JavaScript框架 vue,vue2,vue3都是vue.js的不同版本。 Vue:Vue.js的第一个版本,也称为Vue 1.x。它于2014年首次发布,并获得了广泛的应用和认可。 Vue2:Vue.js的第二个版本,也称为Vue 2.x。它在Vu

    2024年02月12日
    浏览(75)
  • vue3 插槽详解

           在某些场景中,封装组件,我们可能想要为子组件传递一些模板片段,让子组件在它们的组件中渲染这些片段。由此出现了插槽 封装的组件代码如下图所示: 页面对封装组件的应用 注意:为插槽默认指定内容,代码如下图所示 封装组件的代码: 应用组件代码: 运

    2024年02月04日
    浏览(38)
  • vue3插槽的使用

    插槽就是子组件中的提供给父组件使用的一个占位符,用 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的标签。 子组件SlotComponent.vue 父组件 输出结果: 输出结果: 子组件SlotComponent.vue 上段代码中我们添加了 3 个 slot 插槽,

    2023年04月20日
    浏览(34)
  • Vue2向Vue3过度核心技术插槽

    1.作用 让组件内部的一些 结构 支持 自定义 2.需求 将需要多次显示的对话框,封装成一个组件 3.问题 组件的内容部分, 不希望写死 ,希望能使用的时候 自定义 。怎么办 4.插槽的基本语法 组件内需要定制的结构部分,改用****占位 使用组件时, ****标签内部, 传入结构替换slo

    2024年02月11日
    浏览(39)
  • 前端HTML、CSS、JS、VUE3 汇总

    学习https://developer.mozilla.org/zh-CN/docs/Learn/CSS 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 使用VS Code运行前端代码 在VS Code上安装前端插件 正在更新中~ ✨ 提示:这里可以添加本文要记录的大概内容: 学习路线 知识定位 HTML基础 标签、表格、表单、

    2024年02月13日
    浏览(49)
  • Vue3 slot插槽多层传递

    如果你想传递一个slot,从爷到孙的传递, 看了网上的一些方案,依赖注入都来了,其实没那么麻烦 最顶层组件,插入一个按钮到 slot name为 btn的 插槽里面,Button接收一个row的参数,参数可能有多个,这里 用了 { row } 只取 row 在中间组件,这里把插入一个 插槽 插入到 slot n

    2024年02月15日
    浏览(39)
  • 【Vue3 第十九章】插槽 slot

    数字化管理平台 Vue3+Vite+VueRouter+Pinia+Axios+ElementPlus 权限系统-商城 个人博客地址 在某些场景中,我们可能想要为子组件传递一些模板片段,让子组件在它们的组件中渲染这些片段。这就用到了插槽。 插槽是子组件中的提供给父组件使用的一个占位符,用 slot 表示,父组件可以

    2024年02月09日
    浏览(38)
  • Vue3——第十三章(插槽 Slots)

    这里有一个 FancyButton 组件,可以像这样使用: 而 FancyButton 的模板是这样的: slot 元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。 最终渲染出的 DOM 是这样: 通过使用插槽, FancyButton 仅负责渲染外层的 button (以及相应的样式),而

    2024年02月07日
    浏览(51)
  • Vue.js 插槽详解

    插槽允许我们在父组件中定义子组件的模板内容,从而实现动态组件的功能。具体来说,插槽可以用于以下场景: 父组件向子组件传递内容,例如按钮、表单、图片等。 子组件需要显示不同的内容,例如列表、选项卡、面包屑等。 Vue.js 提供了三种类型的插槽:具名插槽、默

    2024年01月20日
    浏览(42)
  • Vue.js 中的插槽是什么?如何使用插槽?

    在 Vue.js 中,插槽是一种组件之间通信的机制,允许父组件向子组件传递内容,并在子组件中进行渲染。本文将介绍 Vue.js 中插槽的概念、优势以及如何使用插槽。 在 Vue.js 中,插槽是一种组件之间通信的机制,允许父组件向子组件传递内容,并在子组件中进行渲染。Vue.js 中

    2024年02月07日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包