vue3官方中文文档:Vue.js : Vue.js - 渐进式 JavaScript 框架 | Vue.js
一、VUE3和VUE2的区别
1、生命周期函数钩子不同
beforeCreate -> setup() 开始创建组件之前,创建的是data和method
created -> setup()
beforeMount -> onBeforeMount 组件挂载到节点上之前执行的函数。
mounted -> onMounted 组件挂载完成后执行的函数
beforeUpdate -> onBeforeUpdate 组件更新之前执行的函数。
updated -> onUpdated 组件更新完成之后执行的函数。
beforeDestroy -> onBeforeUnmount 组件挂载到节点上之前执行的函数。
destroyed -> onUnmounted 组件卸载之前执行的函数。
activated -> onActivated 组件卸载完成后执行的函数
deactivated -> onDeactivated 在组件切换中老组件消失的时候执行
2、数据双向绑定原理不同
VUE2 | VUE3 |
利用ES5的一个APIObject.defineProperty()对数据进行劫持,结合发布者订阅者模式的方式来实现的。 | 使用了ES6的Proxy API对数据代理。 |
vue3提供的proxy API代理的优势在于:
- defineProperty只能监听某个属性,不能对全对象监听
- 可以省去for...in,闭包等内容来提升效率(直接绑定整个对象即可)
- 可以监听数组,不再单独的对数组做特异性处理。可以检测到数组内部数据的变化
3、定义变量和方法不同
vue2 | vue3 |
在data中定义变量,在methods中创建方法 | 使用一个新的setup方法 |
vue3提供的setup方法在组件初始化构造的时候触发,使用以下三个步骤建立反应性数据:
- 从vue引入reactive
- 使用reactive方法来声明数据为响应性数据
- 使用setup方法返回响应性数据,从而template可以获取这些响应式数据
4、API类型不同
vue2 | vue3 |
选项型api(在代码中分割不同属性:data,computed,methods等) | 组合型api(使用方法进行分隔,显得更加简便整洁) |
5、是否支持碎片
vue2 | vue3 |
否 | 是,即可以拥有多个根节点 |
6、父子之间传参不同
vue2 | vue3 |
父传子:子组件通过prop接收子传父:子组件中通过$emit向父组件触发一个监听方法,传递一个参数 | 使用setup()中的第二个参数content对象中有emit,只需要在setup()接收第二个参数中使用分解对象法取出emit就可以在setup方法中随意使用了。 |
7、v-if 和 v-for的优先级
VUE2 | VUE3 |
v-for 优先于 v-if 生效 | v-if 优先于 v-for 生效 |
二、使用Vite创建项目
在VUE3中使用vite来创建项目而不再使用 vue-client ,初始化项目
注意:Vite 需要 Node.js 版本 >= 12.0.0
1、使用npm在淘宝镜像仓库下载全局安装cnpm,再通过cnpm全局安装pnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm i pnpm -g
2、安装vite : 执行下面命令,按照提示完成项目创建
1.输入项目名字,默认为vite-project
2.选择创建的项目类型,选择vue即可
3.选择创建的vue项目类型, javascript
4.启动项目
也可以使用 : npm init vite@latest
3、安装依赖: 进入到创建好的项目中执行命令 pnpm i
pnpm i
cnpm install
4、启动项目
启动命令看 package.json -> scripts { ... }
pnpm dev
通过浏览器访问控制台地址,启动效果如下
三、项目结构
├── dist/ //代码编译目录
└── src/
├── api/ // 接口请求目录
├── assets/ // 静态资源目录
├── common/ // 通用类库目录
├── components/ // 公共组件目录
├── router/ // 路由配置目录
├── store/ // 状态管理目录
├── style/ // 通用样式目录
├── utils/ // 工具函数目录
├── views/ // 页面组件目录
├── App.vue
├── main.js //主JS
├── tests/ // 单元测试目录
├── index.html //默认首页,入口页面
├── jsconfig.json // JavaScript 配置文件
├── vite.config.js // Vite 配置文件
└── package.json //依赖管理
四、route的使用
1、安装route
pnpm i vue-router
2、配置路由
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
#path resolve方法用来获取本地绝对路径
import { resolve } from 'path'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
#别名处理
alias: {
'@': resolve(__dirname, 'src'),
//别名 @相当于src 文件夹目录,如果不配,那么在项目中不能使用@引入页面
//remove i18n waring
},
// why remove it , look for https://github.com/vitejs/vite/issues/6026
// extensions: ['.js', '.ts', '.jsx', '.tsx', '.json', '.vue', '.mjs']
},
//本地代理服务器 跨域处理
server: {
host: '0.0.0.0',
open: true,
proxy: {
// 前缀写法当访问:
'/api': {
target: 'http://localhost:5000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
//scss预处理
css: {
preprocessorOptions: {
scss: {
additionalData: "@import '@/assets/scss/_index.scss';",
},
},
},
})
3、创建页面
在src目录下创建views/order/order.vue 以及 views/user/user.vue 页面文件,内容如下
<template>
<div class="user">我是user</div>
</template>
<script lang="js">
export default {
name: 'User',
components: {},
props: {},
data () {
return {}
},
computed: {},
watch: {},
created () {
},
methods: {}
}
</script>
<style lang="css" scoped>
</style>
4、创建route
在src目录下创建router/index.js文件,内容如下
//导入router
import { createRouter, createWebHashHistory } from "vue-router";
//定义路由配置
const routes = [
{
path:'/user',
component:()=>import('@/views/user/user.vue')
},
{
path:'/order',
component:()=>import('@/views/order/order.vue')
},
//404 修改了 /:pathMatch(.*)匹配所有路由
{
path: '/:pathMatch(.*)',
redirect: '/404'
},
{
path: '/404',
component: () => import('@/views/not-found/not-found.vue'),
hidden: true,
}
]
//创建路由
const router = createRouter({
history: createWebHashHistory(), //hash模式创建路由
routes //导入路由配置
})
//导出配置好的路由
export default router
createWebHashHistory: hash 模式下使用的是createWebHashHistoryapi 进行配置 VueRouter 的历史模式。使用 hash 模式下会在浏览器网页路由当中使用哈希字符(#)对 url 进行切割并且匹配哈希字符后的字符进行判断路由匹配与跳转处理
5、在main.js中使用路由
...省略...
//导入路由
import router from './router'
//创建app
const app = createApp(App);
//使用路由
app.use(router)
//挂载app
app.mount('#app')
6、在App.vue文件中指定路由出口
<template>
<div>
<!-- 路由出口 ,所有页面都会放到这个里面-->
<router-view></router-view>
</div>
</template>
7、重启测试
五、Stup函数
1、普通语法
<template>
<div class="user">{{title}}</div>
<button @click="met">我是按钮</button>
</template>
<script type="js">
export default {
setup(){
//定义全局属性
var title = "我是user";
//定义函数
const met = function (){
console.log("met被点击了..")
}
//返回数据
return {
title,met
}
}
}
</script>
2、语法糖(推荐)
<template>
<div class="user">{{title}}</div>
<button @click="met">我是按钮</button>
</template>
<script setup>
// 定义标题
const title = "我是标题";
//定义方法
const met = ()=>{
console.log("met被点击了..")
}
</script>
注意:在setup语法中使用 this 会出现unidefined , 在 setup 中你应该避免使用 this,因为它不会找到组件实例。setup 的调用发生在 data property、computed property 或 methods 被解析之前,所以它们无法在 setup 中被获取,这也是为了避免setup()和其他选项式API混淆
六、响应式数据
1、基础数据响应式函数
VUE3提供了ref函数可以在修改完 数据后自动渲染视图,类似余双向绑定
- 在js中通过 import {ref} from "vue"; 来声明 ref函数
- 注意:ref属性在js中要使用.value 取值
<template>
<div class="user">{{refValue}}</div>
<button @click="changeValue">我是按钮</button>
</template>
<script setup>
import {ref} from "vue";
//使用ref函数包括内容
let refValue = ref(111);
//修改变量的值,模版中会跟者变
const changeValue = ()=>{
refValue.value = 222;
}
</script>
通过ref获取dom对象
<template>
<input ref="inputRef" :value="refValue"/>
</template>
<script setup>
import {ref} from "vue";
//变量名 inputRef 和 dom元素上的ref="inputRef" 一样,就能获取到dom元素
let inputRef = ref(null);
console.log(inputRef);
</script>
2、reactive数据响应
对于对象类型可以使用reactive进行响应式函数处理
<template>
<h5>{{reactiveValue}}</h5>
<button @click="changeReactiveValue">我是按钮</button>
</template>
<script setup>
import {reactive, ref} from "vue";
const reactiveValue = reactive({
username:"zs",
age:11
});
const changeReactiveValue = ()=>{
//修改reactiveValue对象的username的值
reactiveValue.username = "ls"
}
console.log(reactiveValue);
</script>
3、toRef解析响应式
<template>
<h5>{{reactiveValue}}</h5>
<button @click="changeReactiveValue">我是按钮</button>
</template>
<script setup>
import {reactive, ref} from "vue";
const reactiveValue = reactive({
username:"zs",
age:11
});
//修改username为ls是具备响应式的
reactiveValue.username = "ls"
const username = reactiveValue.username;
//单独提取的变量不具备响应式
username = "ww";
</script>
4、toRefs 批量解析响应式
let {username,age} = toRef(reactiveValue);
七、computed计算函数
通过计算函数演示2个值相加,需要通过 import {computed} from "vue"; 引入函数
<template>
<div class="computed">
<h1>computed</h1>
<h5>{{num1}} + {{num1}} = {{sum}}</h5>
</div>
</template>
<script setup>
import {computed, ref} from "vue";
const num1 = ref(10);
const num2 = ref(20);
const sum = computed(()=>{
return num1.value + num2.value;
})
</script>
八、watch监听器
1、watch监听器
watch的作用是用来监听某个值的变化从而出发watch回调
<template>
<div class="watch"></div>
<button @click="value++">修改value</button>
<button @click="persion.age++">修改persion</button>
</template>
<script setup>
import {reactive, ref, watch} from "vue";
const value = ref(1111);
const persion = reactive({
username:"zs" ,
age:18
})
//监听单个值
watch(value,()=>{
console.log(value);
})
//监听对象
watch(persion,()=>{
console.log(persion);
})
//监听多个,用[]包含多个
watch([persion,value],()=>{
console.log(persion,value);
});
//监听对象的属性
watch(persion.age,()=>{
console.log(persion);
});
</script>
2、watchEffect监听副作用
watchEffect的作用是只要使用到的数据都会被监听到,无需指定具体监听谁,他包含4个功能
- 页面加载,watchEffect的回调就会立即执行
- 只要watchEffect 只要里面使用了任何数据,就会触发回到函数
- onCleanup函数可以清除副作用
- 返回一个stopHandler可以停止监听
<template>
<div class="watch_effect"></div>
<button @click="num++">num++</button>
<button @click="stopHandler">StopWatch</button>
</template>
<script setup>
import {ref, watchEffect} from "vue";
const num = ref(1111);
//1.页面加载,初始化立即回执行回调
//2.只要watchEffect 只要里面使用了数据,就会触发回到函数
//3.onCleanup函数可以清除副作用
//4.返回一个stopHandler可以停止监听
/**
watchEffect(()=>{
console.log(num.value);
})
**/
const stopHandler = watchEffect(onCleanup=>{
console.log(num.value);
onCleanup(()=>{
console.log("清除副作用");
});
})
</script>
九、父子组件
1、父传参数给子
第一步:创建一个父组件页面,引入子组件,并传入参数 parent.vue
<template>
<H1 class="parent_child">父传子</H1>
<h2>使用子组件传参</h2>
<child-vue text="传入静态文本" :num="num" :title="title" :persion="persion"></child-vue>
</template>
<script setup>
//引入子组件取名用驼峰,页面用中划线 <child-vue></child-vue>
import ChildVue from "@/views/parent_child/child.vue";
import {reactive, ref} from "vue";
//构建一个响应式对象
const persion = reactive({
username:"zs",
age:18
})
//定义响应式变量
const title = ref("我是标题");
const num = ref(111);
</script>
第二步:创建一个子组件页面 : child.vue , 通过 defineProps接受父传入的参数
<template>
<h2>text:{{text}}</h2>
<h2>num:{{num}}</h2>
<h2>title:{{title}}</h2>
<h2>persion:{{persion}}</h2>
</template>
<script setup>
//通过defineProps来接受父传入过来的参数
const prop = defineProps({
text:String,
num:Number,
title:String,
persion:Object
})
//在js中可以读取父传入过来的参数,但是不能修改
console.log("prop.title" , prop.title)
</script>
注意:在子组件中没法修改父转件传入的参数
2、子传参给父
子组件给父组件传值,需要通过defineEmits在子组件声明事件方法,然后调用该事件方法传值,父组件需要监听该事件方法取值。第一步:在子组件中准备一个按钮,点击按钮就给父组件传值。然后给按钮绑定一个方法。
<h1>给父组件传参</h1>
<button @click="sendData2Parent">给父组件传参</button>
------------------------------------------------------------------------------------------
//声明事件
const $emits = defineEmits(["data2parent","事件名","事件名"]);
const sendData2Parent = ()=>{
//调用声明的事件
$emits('data2parent',"传给父组件的数据");
}
- const $emits = defineEmits(["data2parent","事件2","事件3"]); 自定义了三个事件
- $emits('data2parent',"传给父组件的数据"); 调用自定义的事件
第二步:在父组件中绑定该事件,并调用一个函数来接受值
<child-vue @data2parent="getDataFromChild" ...></child-vue>
const getDataFromChild = (data)=>{
console.log("打印数据",data);
}
- @data2parent="getDataFromChild" : data2parent对应了子组件中声明的事件,拿到数据后交给getDataFromChild去处理。
3、子暴露
VUE3提供了 defineExpose 来暴露子组件中的数据和方法在父组件中可以拿到子组件中暴露的数据或者方法。第一步在子组件中暴露数据
//子暴露
defineExpose({
data:"我是子暴露的数据",
sayHi:()=>{
return "我是子暴露的方法被调用返回的结果";
}
})
第二步:在父组件接受子暴露的数据
<child-vue
ref="childVueRef" ...></child-vue>
//用一个响应式变量,接受子暴露的数据
const childVueRef = ref(null);
onMounted(()=>{
console.log("子暴露的数据:",childVueRef.value.data);
console.log("子暴露的方法:",childVueRef.value.sayHi());
})
- ref="childVueRef" : 接受子暴露,把数据绑定给childVueRef
注意:因为在父组件的 setup 函数中,声明周期很早,此时子组件还没加载,所以需要在onMounted去调用子暴露的数据和方法才可以拿到结果
十、自定义hooks函数
hooks就是用来给我们抽取公共代码的,hooks函数就是通过 use开头的js参数
<template>
<div class="hooks"></div>
<div>屏幕宽度:{{w}}</div>
</template>
<script setup>
//获取屏幕宽度
import {onMounted, onUnmounted, ref} from "vue";
const w = ref(window.innerWidth);
const $resize = ()=>{
//获取屏幕宽度
const width = document.body.clientWidth;
//赋值给W
w.value = width;
}
onMounted(()=>{
//监听窗口的resize事件
window.addEventListener("resize",$resize)
})
onUnmounted(()=>{
//监听窗口的resize事件
window.removeEventListener("resize",$resize)
})
</script>
在src目录下创建一个 hooks目录,然后在里面创建一个 useWindowWidth.js 文件
import {onMounted, onUnmounted, ref} from "vue";
//暴露一方法:useWindowWidth
export const useWindowWidth = ()=>{
//初始化一个 w 代表屏幕宽度
const w = ref(window.innerWidth);
//窗体改变,重新获取宽度,并赋值给w
const $resize = ()=>{
//获取屏幕宽度
const width = document.body.clientWidth;
//赋值给W
w.value = width;
}
onMounted(()=>{
//监听窗口的resize事件
window.addEventListener("resize",$resize)
})
onUnmounted(()=>{
//移除监听窗口的resize事件
window.removeEventListener("resize",$resize)
})
return {w};
}
<template>
<div class="hooks"></div>
<div>屏幕宽度:{{w}}</div>
</template>
<script setup>
import {useWindowWidth} from "@/hooks/useWindowWidth.js";
//调用函数,拿到屏幕宽度
const {w} = useWindowWidth();
</script>