持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
目前大部分的项目还在使用Vue2.x,感觉落伍了。技术更新得太快,太卷了,不学不行呀!本文是在学习技术胖的课程后写下的学习笔记,方便以后查阅。好记性不如烂笔头,写过笔记印象深刻很多。这是基础入门篇,不喜轻喷。
一. 脚手架搭建
查看vue版本
npm install -g @vue/cli
yarn global add @vue/cli //vue -version 版本必须大于4.5.6
vue create vue3-demo
也可以通过vue ui可视化创建
二. 项目初始结构和文件分析
-node_modules -- 依赖包
|-public -- 公共文件夹
---|favicon.ico -- 网站的显示图标
---|index.html -- 入口的html文件
|-src -- 源文件目录
---|assets -- 放置静态文件的目录
---|components -- Vue的组件文件
---|App.vue -- 根组件
---|main.ts -- 入口文件
---|shims-vue.d.ts -- 类文件(也叫定义文件),因为.vue结尾的文件在ts中不认可,所以要有定义文件
|-.eslintrc.js -- Eslint的配置文件,不用作过多介绍
|-.gitignore -- 用来配置那些文件不归git管理
|-package.json -- 命令配置和包管理文件
-README.md -- 项目的说明文件,使用markdown语法进行编写
|-tsconfig.json -- 关于TypoScript的配置文件
|-yarn.lock -- 使用yarn后自动生成的文件,由Yarn管理,安装yarn包时的重要信息存储到yarn.lock文件中
三. setup()和ref()函数
ref一般用来声明基础类型,比如string,number,boolean。也可以声明引用类型数据,array,object,但其本质内部依然采用了raective声明。如果在声明数据的时候,不使用ref 或者 reactive 则数据不会响应。
<template>
<div>{{ name }}</div>
<button @click="changeName('开始学习Vue3')">修改name值</button>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
export default defineComponent({
name: "App",
setup() { //setup函数可以代替Vue2的data和methods属性
const name = ref("初识Vue3");
const changeName = (newName: string) => {
name.value = newName; //注意使用ref重新赋值要在.value上操作
};
return { //return出去的数据和方法可以在模板中使用
name,
changeName
};
},
});
</script>
四. reactive和toRefs的使用方法
reactive针对于引用类型数据的声明,array,object。
<template>
<ul>
<li
v-for="(item, index) in course"
:key="index"
@click="selectCourseFun(index)"
>
{{ item }}
</li>
</ul>
<div class="currentSelected">{{ selectCourse }}</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from "vue";
export default defineComponent({
name: "App",
setup() {
interface DataProps {
//给data添加类型注解
course: string[];
selectCourse: string;
selectCourseFun: (index: number) => void;
}
const data: DataProps = reactive({
course: ["vue3进阶", "typeScript基础", "ES6高级"],
selectCourse: "",
selectCourseFun: (index: number) => {
data.selectCourse = data.course[index]; //不需要加.value
},
});
return {
...toRefs(data), ////使用toRefs转换,然后扩展,不然在模板上需要data.course这样使用
};
},
});
</script>
五. setup语法糖代码更精简
如果不喜欢每个变量或方法都要return才能在模板上使用,可以用setup语法糖。
<template>同上</template>
<script setup lang="ts">
import { ref, reactive, toRefs } from "vue";
const name = ref("初识Vue3"); //不需要retutn,可以直接在模板上使用
const changeName = (newName: string) => {
name.value = newName;
};
interface DataProps {
//给data添加类型注解
course: string[];
selectCourse: string;
selectCourseFun: (index: number) => void;
}
const data: DataProps = reactive({
course: ["vue3进阶", "typeScript基础", "ES6高级"],
selectCourse: "",
selectCourseFun: (index: number) => {
console.log(index);
console.log(data);
data.selectCourse = data.course[index];
},
});
const { course, selectCourse, selectCourseFun } = toRefs(data); //使解构后的数据重新获得响应式
</script>
六. Vue3.x的生命周期和钩子函数
setup() //开始创建组件之前,在beforeCreate和created之前执行。创建的是data和method
onBeforeMount() // 组件挂载到节点上之前执行的函数。
onMounted() //组件挂载完成后执行的函数。
onBeforeUpdate() //组件更新之前执行的函数。
onUpdated() //组件更新完成之后执行的函数。
onBeforeUnmount() //组件卸载之前执行的函数。
onUnmounted() //组件卸载完成后执行的函数
onActivated() //被包含在<keep-alive>中的组件,会多出两个生命周期钩子函数。被激活时执行。
onDeactivated() //比如从 A 组件,切换到 B 组件,A 组件消失时执行。
onErrorCaptured() //当捕获一个来自子孙组件的异常时激活钩子函数(以后用到再讲,不好展现)。
onRenderTracked() //状态跟踪,它会跟踪页面上所有响应式变量和方法的状态,也就是我们用`return`返回去的值
onRenderTriggered() //状态触发。跟踪变化的值
vue2.x和vue3.x生命周期对比
Vue2--------------vue3
beforeCreate -> setup()
created -> setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
activated -> onActivated
deactivated -> onDeactivated
errorCaptured -> onErrorCaptured
使用方式:
<script lang="ts">
import { defineComponent, onMounted, onBeforeMount } from "vue";
export default defineComponent({
name: "App",
setup() {
onBeforeMount(() => {
console.log("onBeforeMount");
});
onMounted(() => {
console.log("onMounted");
});
},
});
</script>
七. onRenderTracked()和 onRenderTriggered()钩子函数的使用
Vue3.x新增了onRenderTracked和onRenderTriggered钩子函数。
onRenderTracked直译过来就是状态跟踪,它会跟踪页面上所有响应式变量和方法的状态,也就是我们用return返回去的值,他都会跟踪。函数只会在页面渲染的时候执行一次,局部更新不执行。
<script lang="ts">
import { .... ,onRenderTracked,} from "vue";
setup() {
onRenderTracked((event) => {
console.log("状态跟踪组件----------->");
console.log(event);
});
}
</script>
onRenderTriggered直译过来是状态触发,它不会跟踪每一个值,而是给你变化值的信息,并且新值和旧值都会给你明确的展示出来。
<script lang="ts">
import { .... ,onRenderTriggered,} from "vue";
setup() {
onRenderTriggered((event) => {
console.log("状态跟踪组件----------->");
console.log(event);
//event 对象属性的详细介绍
//key 那边变量发生了变化
//newValue 更新后变量的值
//oldValue 更新前变量的值
//target 目前页面中的响应变量和函数
});
}
</script>
八. Vue3中watch的使用和注意事项
监听单个值,引入后编写watch函数,它接受两个参数,第一个是要监听的值,这里是course,然后是一个回调函数。在函数中你可以获得到新值和旧值。
<template>
<div>当前课程:{{ course }}</div>
<button @click="changeCourseState">修改课程状态</button>
</template>
<script lang="ts">
import { defineComponent, ref, watch } from "vue";
export default defineComponent({
name: "HomeView",
setup() {
const course = ref("Javascript");
const changeCourseState = () => {
course.value = course.value + "学习完成|";
};
watch(course, (newValue, oldValue) => {
//使用watch监听course变化,在回调函数中获取新旧值
console.log(`new--->${newValue}`);
console.log(`old--->${oldValue}`);
});
return {
course,
changeCourseState,
};
},
});
</script>
监听多个值,当你要监听多个值的时候,不是写多个watch函数,而是要传入数组的形式。
<script lang="ts">
import { defineComponent, ref, reactive, toRefs, watch } from "vue";
export default defineComponent({
name: "HomeView",
setup() {
interface DataProps {
//给data增加类型注解
courseList: string[];
selectCourse: string;
selectCourseFun: (index: number) => void;
}
const data: DataProps = reactive({
courseList: ["vue3初识", "Node.js进阶", "css世界"],
selectCourse: "vue3初识",
selectCourseFun: (index: number) => {
data.selectCourse = data.courseList[index];
},
});
const course = ref("Javascript");
const changeCourseState = () => {
course.value = course.value + "学习完成|";
};
////不是直接data.selectCourse,因为watch不能监听没有getter/effect方法。可以使用箭头函数直接返回。
watch([course, () => data.selectCourse], (newValue, oldValue) => {
console.log(`new--->${newValue}`);
console.log(`old--->${oldValue}`);
});
return {
...toRefs(data),
course,
changeCourseState,
};
},
});
</script>
可以看到监听多个值时候,新旧值会以逗号隔开。
九. Vue3中模块化介绍
//模块 useNowTime.ts
import { ref } from 'vue'
const nowTime = ref("00:00:00");
const getNowTime = () => {
const now = new Date();
const hour = now.getHours() < 10 ? String(now.getHours()).padStart(2, "0") : now.getHours();
const minu = now.getMinutes() < 10 ? "0" + now.getMinutes() : now.getMinutes();
const sec = now.getSeconds() < 10 ? "0" + now.getSeconds() : now.getSeconds();
nowTime.value = hour + ":" + minu + ":" + sec;
setTimeout(getNowTime, 1000); //每一秒执行一次这个方法
}
export { nowTime, getNowTime } //导出
<template>
<div>{{ nowTime }}</div>
<div><button @click="getNowTime">显示时间</button></div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { nowTime, getNowTime } from "../hooks/useNowTime"; //引入模块
export default defineComponent({
name: "HomeView",
setup() {
return {
nowTime,
getNowTime,
};
},
});
</script>
使用axios获取图片
// 模块useUrlAxios.js
import { ref } from 'vue'
import axios from 'axios' //需要先安装依赖 yarn add axios 或 cnpm i axios -S
function useUrlAxios(url: string) {
const result = ref(null)
const loading = ref(true)
const loaded = ref(false)
const error = ref(null)
axios.get(url).then((res) => {
loading.value = false;
loaded.value = true;
result.value = res.data;
}).catch(e => {
error.value = e;
loading.value = false;
})
return {
result,
loading,
loaded,
error
}
}
export default useUrlAxios
<template>
<div>
<div v-if="loading">Loading.....</div>
<img v-if="loaded" :src="result.message" />
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import useUrlAxios from "../hooks/useUrlAxios"; //引入模块
export default defineComponent({
name: "HomeView",
setup() {
const { result, loading, loaded } = useUrlAxios(
"https://dog.ceo/api/breeds/image/random"
);
return { result, loading, loaded };
},
});
</script>
十. Teleport瞬间移动函数的使用(独立函数)
Teleport在国内大部分都翻译成了瞬间移动组件,个人觉得不太好理解。我把这个函数叫独立组件,它可以把你写的组件挂载到任何你想挂载的DOM上,所以说是很自由很独立的。 在使用Vue2的时候是作不到的。
// 新建组件 newDialog.vue
<template>
<teleport to="#modal">
//组件挂载在modal节点上
<h2>自定义Dialog组件</h2>
</teleport>
</template>
// 在public/index.html增加一个Modal节点
<div id="app"></div> <div id="modal"></div>
<template>
<div>
//组件挂载在app节点上
<h2>原节点</h2>
</div>
<new-dialog></new-dialog>
</template>
<script lang="ts">
import newDialog from "@/components/newDialog.vue"; //引入组件
const app = {
name: "HomeView",
components: {
newDialog,
},
};
export default app;
</script>
Teleport方法,可以把Dialog组件渲染到你任意想渲染的外部Dom上,不必嵌套再#app里了,这样就不会互相干扰了。你可以把Teleport看成一个传送门,把你的组件传送到你需要的地方。 teleport组件和其它组件没有任何其它的差异,用起来都是一样的。
十一. Suspense 初识异步请求组件
在Vue2.x时代,判断异步请求的状态是一件必须的事情,但是这些状态都要自己处理,根据请求是否完毕展示不同的界面。尤大神深知民间饥苦,在Vue3.x中给我们提供了Suspense组件。
<suspense> 组件有两个插槽。它们都只接收一个直接子节点。default 插槽里的节点会尽可能展示出来。如果不能,则展示 fallback 插槽里的节点。
Demo1
//新建组件 AsyncShow.vue
<template>
<h1>{{ result }}</h1>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
setup() {
return new Promise((resolve, reject) => {
//模拟异步请求数据
setTimeout(() => {
return resolve({ result: "已经延迟两秒了,我要出来了" });
}, 2000);
});
},
});
</script>
<template>
<div>
<Suspense>
<template #default>
<AsyncShow></AsyncShow>
</template>
<template #fallback>
<h1>loading....</h1>
</template>
</Suspense>
</div>
</template>
<script lang="ts">
import AsyncShow from "@/components/AsyncShow.vue"; //引入组件
export default {
name: "HomeView",
components: { AsyncShow },
setup() {
return {};
},
};
</script>
Demo2
//新建组件 DogShow.vue
<template>
<img :src="result" alt="" />
</template>
<script lang="ts">
import axios from "axios";
import { defineComponent } from "vue";
export default defineComponent({
async setup() {
//使用async await代替promise
const rowData = await axios.get("https://dog.ceo/api/breeds/image/random");
return { result: rowData.data.message };
},
});
</script>
<template>
<div>
<Suspense>
<template #default>
<DogShow></DogShow>
</template>
<template #fallback>
<h1>loading....</h1>
</template>
</Suspense>
</div>
</template>
<script lang="ts">
import DogShow from "@/components/DogShow.vue"; //引入组件
export default {
name: "HomeView",
components: { DogShow },
setup() {
return {};
},
};
</script>
onErrorCaptured钩子函数
在捕获一个来自后代组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。【传送门】
Demo2 组件DogShow.vue稍微修改一下,故意把接口写错"dog.ceo/api/breeds/…" 。
<template>
<div>
<Suspense>
<template #default>
<DogShow></DogShow>
</template>
<template #fallback>
<h1>{{ currentState }}</h1>
</template>
</Suspense>
</div>
</template>
<script lang="ts">
import { ref, onErrorCaptured } from "vue";
import DogShow from "@/components/DogShow.vue";
export default {
name: "HomeView",
components: { DogShow },
setup() {
const currentState = ref("loading....");
//捕获后代组件报错信息
onErrorCaptured((error, instance, info) => {
console.log(`error====>`, error);
console.log(`instance====>`, instance);
console.log(`info====>`, info);
currentState.value = "异步数据请求失败!";
return true;
});
return {
currentState,
};
},
};
</script>