创建项目
* vue cli创建 ==> vue create 项目名称
* vite创建 ===> npm init vite-app 项目名称
vite优势
1。快速冷启动 vite先启动服务,,根据客户端的请求加载不同的模块处理,实现按需加载 冷启动时不会打包全部项目文件代码。
webpack则是,一开始就先分析分析各个模块之间的依赖将整个项目都打包一遍,再开启dev-server,如果项目规模庞大,模块太多,打包时间必然很长,会造成打包的bundle体积很大,热更新速度很慢。
2.模块热更新:采用立即编译当前修改文件的办法,让浏览器重新请求该模块即可,同时结合vite的缓存机制(http缓存 ==》 Vite内置缓存),加载更新后的文件内容。不像webpack那样需要把该模块的相关依赖模块全部编译一次
3.打包编译速度快 使用esbuild预构建依赖,而esbuild是用Go编写的,go比JS编写的打包器快很多
vue2 options api / vue3 组合式 API
vue2 options api(computed ,data,methods) 约定了该在哪个位置做什么事,这在一定程度上也强制我们进行了代码分割,大型组件中,数据与方法会很多,必须不断地在选项代码块之间“跳转”,以找到与该关注点相关的部分,理解和维护复杂组件变得困难
Composition API提供了一个setup函数,我们可以将data数据、计算属性、方法等等,都放在setup函数中,这样就可以对业务进行集中处理了
setup
- 在beforeCreate之前执行。beforeCreate有this,但是获取不到data的值,所以setup 中是不能使用 this 的,
- 在vue2的配置中可读取到vue3配置中的属性和方法
- 组合式 API 入口点 它会在Vue实例创建完成前被调用。所以,setup函数中没有this指针undefined 但是getCurrentInstance可以替代this但不推荐
- 只能同步使用setup函数,不能用async将其设为异步。
- 如果 setup 返回一个对象,则对象的属性将会被合并到组件模板的渲染上下文,我们就可以在模板里使用对应的属性和方法
setup语法糖
<script setup lang="ts"></script>
- setup语法糖就可以不用写setup函数了。并且,数据和函数也不用返回,引入的组件也不需要通过通过 components注册了,
< script setup>语法糖里面的代码会被编译成组件 setup() 函数的内容- 组件通信: 父子组件直接传值逻辑更清晰了(defineProps,defineEmits)。
- 父组件是无法通过ref 或者 $parent 获取到子组件的属性,需要先通过defineExpose 主动暴露,defineExpose+ref
setup接收参数(props ,context)
- props 里面包含父组件传递给子组件的所有数据,子组件中使用 props 进行接收. 且props 是响应式的,所以不能使用 ES6 解构,它会消除 prop 的响应性。如果需要解构 prop,可以使用 toRefs 函数来完成
- context 里面包含 attrs, slots, emit 等数据方法:
- attrs:获取组件上的属性 slots:获取 slot 插槽的节点 emit :emit 方法(子组件向父组件传递数据)
.ref reactive toRef 和 toRefs
- .ref 响应式监听 然后可以使用.value访问或更改响应式变量的值: 解决.value问题 reactive 与 toRefs 1.首先使用 reactive 创建响应式对象state,封装数据 2.在返回时使用 …toRefs(state) 因为toRefs(state) 将state对象展开,并包装成多个响应数据。
- toRef 和 toRefs 都可以用来复制 reactive 里面的属性然后转成 ref,复制的属性修改后,除了视图会更新,原有 reactive 里面对应的值也会跟着更新
- toRef: 复制 reactive 里的单个属性并转成 ref toRef(state, ‘name’);
- toRefs: 复制 reactive 里的所有属性并转成 ref 响应式拆解
ref和reactive定义响应式数据的区别
- reactive仅对复杂类型有效包括数组,对象,对原始类型无效(主要的原因在于在 vue 内部的响应式的实现,是依据 proxy 实现的,但是 proxy 不适用于基本类型数据)
- ref支持原始类型和对象 基本类型响应式依赖Object.defineProperty( ),如果ref使用的是引用类型,底层ref会借助reactive的proxy 定义响应式
- 当 ref 在模板中使用时,作为顶层 property 被访问时,它们会被自动“解包”,所以不需要使用 .value
provide与inject
- provide与inject可用于多级组件直接传递数据 以解决 props 深度透传的问题,小项目不想使用vux的时候
- provide(“money”, num); // 组件提供了money属性,在子组件中使用 const money = inject(‘money’)接收money
- provide(“changeMoney”, changeMoney);//子孙组件修改父组件的数据,通过传递一个方法过去
- provide和inject用于跨组件通信,但是它们不能直接传递动态数据。Vue实例的provide选项需要在Vue实例挂载之前就确定,因此不能直接传递一个响应式的数据。解决办法:可以通过data函数返回一个响应式对象,并在子组件中通过inject选项来接收 或者 e m i t 和 emit和 emit和on)或者Vuex
生命周期
- setup在beforeCreate之前执行。创建的是 data 和 method
- 在Composition API中没有beforeCreate()和created()这里两个声明周期函数了,统一都使用setup()。
- onBeforeMount,onMounted,挂载前后,
- onBeforeUpdate,onUpdated
- onUnmounted 卸载组件实例后调用
路由跳转,路由参数获取
const router = useRouter();
const route = useRoute();
router.push({path:'/home', query:{id:'12' }})
route.query.id
vueX
获取Dom节点
watch和watchEffect
- watch需要明确监听哪个属性,watchEffect会根据其中的属性,自动监听其变化
- watchEffect初始化时,一定会执行一次,且只能获取变化后的值,watch需要另外设置才会执行
父组件
<template>
<div>
<button @click="clickFun">按钮</button>
<div>{{ msg }}</div>
<page1 :num="num">
插槽里的内容
<template v-slot:footer>
<p>多个插槽时候使用具名插槽</p>
</template>
</page1>
</div>
</template>
<script setup>
//setup语法题写法
//defineProps 获取父组件传值
//defineEmits 触发父组件事件传值
import { inject, ref, toRefs, watchEffect, provide } from "vue";
import page1 from "./home.vue";
//const props = defineProps(["msg"])
const props = defineProps({
msg: {
type: String,
required: true,
default: "",
},
age: Number,
});
let num = ref(10);
const changeMoney = (n) => {
num.value = n;
};
provide("money", num); // 组件提供了money属性,在子组件中使用 const money = inject('money')接收money
provide("changeMoney", changeMoney);//子孙组件修改父组件的数据,通过传递一个方法过去
const emit = defineEmits(["subInfo", "subItem"]);
const clickFun = () => {
console.log(props.msg);
num.value++;
emit("subInfo", num.value);
};
</script>
子组件
<template>
<div>
<p>money=====>{{money}}</p>
<button @click="fn">home组件修改</button>
{{num}}
<slot></slot>
<slot name="footer"></slot>
</div>
</template>
<script setup>
import {ref, toRefs ,watchEffect,inject} from "vue"
const props = defineProps({
num:Number
})
const money = inject('money')
const changeMoney = inject('changeMoney')
const fn = () => {
changeMoney(200000) // 子传父,使用changeMoney方法 修改money的值
}
//const emit = defineEmits(["subInfo", "subItem"])
// let num = ref(10);
// const clickFun =()=>{
// console.log(props.msg)
// num.value++
// emit("subInfo", num.value)
// }
</script>
<template>
<div class="hello">
<p ref="myRef">count----{{ count }}</p>
</div>
</template>
<script>
import { ref, computed, watch, watchEffect, reactive, toRef,toRefs,onBeforeMount } from "vue";
export default {
name: "HelloWorld",
props: ["msg"],
//因为 props 是响应式的,你不能使用 ES6 解构,它会消除 prop 的响应性。如果需要解构 prop,可以使用 toRefs 函数来完成
setup(props,context) {
console.log(props);
console.log(context);
//使用 reactive 创建响应式对象,封装数据,在return时使用 ...toRefs(state)
const state = reactive({
number: 124,
doubleCount: computed(() => state.number *2),
});
//computed计算属性 它接受 getter 函数并为 getter 返回的值返回一个不可变的响应式 ref 对象
let num1 = ref(4);
const msgStr = ref(props.msg);
const userID = ref(2222);
//获取dom节点
const myRef = ref(null);
const count = computed({
get: () => {
console.log("computed get");
return state.number + 100;
},
set: (val) => {
console.log("computed set");
console.log(val);
},
});
watch(
num1,
(newVal, oldVal) => {
console.log(newVal, "新的");
console.log(oldVal, "旧的");
},
{ immediate: true, deep: true }
);
//监听对象中的某个具体数据,vue做了优化,使用 watch 监听 reactive 的时候可以不添加 deep 属性,也能够对其做深度监听
watch(()=>state.number, (newVal, oldVal) => {
console.log(newVal, oldVal)
});
//监听多个数据 注意多个同步更改只会触发一次侦听器。
watch([()=>state.number,num1],(newVal,oldVal)=>{
console.log(newVal, oldVal)//[124, 123] => [124, 4]
})
//watchEffect 会返回一个用于停止这个监听的函数
const stop = watchEffect(() => {
console.log('watchEffect',count.value)
console.log('watchEffect',msgStr.value)
});
//stop()
function edit() {
console.log(props.msg);
context.emit('childFun','子组件的值')
console.dir(myRef.value);
}
return {
fun: () => {
num1.value = 123;
},
...toRefs(state),
};
},
};
</script>
vue2和3响应式原理
1.ref 被ref函数包裹基本数据类型 返回一个 ref 引用实例对象,被包裹的对象它内部会通过 reactive函数转为代理对象
2.reactive reactive包裹一个对象或数组,返回一个被包裹后的proxy代理对象
3.vue2.0响应式存在的问题:通过对 object.defindProperty() 对属性的读取 修改进行拦截,如果新增属性,删除属性也不会进行更新。需要nextTick处理下
4.vue3通过 Proxy代理,拦截对象中任意属性的变化,包括对象的新增属性,删除属性没有这个问题
自定义hooks函数
用hooks来实现类似vue2的mixins的功能。其实是函数的写法,就是将文件的一些单独功能的js代码进行抽离出来,复用功能代码的来源, 更清晰易懂
1.首先应该先在src下建立一个hooks文件夹:其目的是为了存放hook文件。
2.建立相关的hook文件:一般使用use开头。
3.使用
import useCounter from “./hooks/useCounter.js”;
const { user, hooksFun } = useCounter();