vue3 代号 onepiece 【海贼王】
2022年2月7日 成为默认版本 , 2023年底
vue2将彻底淘汰。
真的对 类型 特别特别 较真的用 React 也没什么不好,无非就是性能差点。
proxy 代理 | 虚拟dom (diff算法 + 静态标记) |
---|---|
采用 ES6 proxy对象 去数据监听 实现原理 => 点击学习 | 对可能变化的dom 进行静态标记 只监听标记了的 |
① vue 脚手架 创建 | ② Vite 创建 |
---|---|
#安装或升级 => npm install -g @vue/cli #创建项目 => npm create vue_test | #创建项目 => npm init vite-app vue-test2 |
setup函数 | setup单文组件 |
---|---|
1.其中的变量和方法,需要return 才能使用 | 1.单文组件中的 变量不需要 retrun |
2.不要对响应数据解构,防止响应失效 | 2.只要是reactive和ref 创建的就可以数据响应 |
3.通过函数形参,获取父子组件通信对象 (props 、emit) | 3.通过内置函数去访问,来组件通信 (defineProps、defineEmits ) |
Ⅰ.setup 函数 【响应数据写法、父子通信写法】=>
父组件 : |
<templete>
<Children :fatherName = 'fatherName' @handle = 'fun' />
templete>
<script>
export default {
setup(){
function fun(num){ console.log('接受子组件传递过来的值'+num ); }
return {
fun,
fatherName: '张xx',
}
}
}
script>
子组件 ( Children ) : |
<templete>
<span> {{ name }} span>
<span> {{ age }} span>
<span> {{ fatherName }} span>
templete>
<script>
import { reactive , toRefs } from 'vue';
export default {
props:{
fatherName:{ type:String }
},
setup(props,{ emit }){
console.log( props.fatherName ); // => 获取父组件 传递 子组件的参数
emit('handle' , 123); // => 子组件 委托 父组件 调用方法接受参数
const state = reactive({ name: '张三', age: 18 })
return {
//...state => 会失去响应
...toRefs(state) // => 正确写法
...toRefs(props)
}
}
}
script>
setup 单文组件 【响应数据写法、父子通信写法】=>
父组件 : |
<templete>
<Children :fatherName = 'fatherName' @handle = 'fun' />
templete>
<script setup>
export default {
const fun = (num) =>{ console.log('接受子组件传递过来的值'+num ); }
const fatherName = '李xx',
}
script>
子组件 ( Children ) : |
<templete>
<span> {{ state.name }} span>
<span> {{ state.age }} span>
<span> {{ fatherName }} span>
templete>
<script setup>
import { reactive } from 'vue';
const state = reactive({name:'李四', age:20 });
const { fatherName } = defineProps({ fatherName : { type : String }, }) ; // => 获取父组件 传递 子组件的参数
const emit = defineEmit(['handle']);
emit('handle', 123 ) // => 子组件 委托 父组件 调用方法接受参数
script>
注意事项 : proxy.$parent 和 ref 只能 获取和调用 子或父 setup函数 return 中的 。
<templete>
<Children ref = 'children' />
templete>
<script>
import { getCurrentInstance , ref } from 'vue';
export default {
setup(){
const { proxy } = getCurrentInstance();
console.log( proxy.$parent ); // => 获取父组件的 return 中的所有对象 .
const children = ref(null);
// 变量名要和组件的ref值 对应 , 赋值必须为 ref(null) .
console.log(children.value); //=> 获取父组件的 return 中的所有对象 .
}
}
script>
3 . 也可以通过 provide 、inject 传递方法和变量 ;
注意事项:provide 、inject 一个接收一个传递 ,只能从 父 => 子 => 孙 => … ( 从上往下单向传递 )
①父组件:
import { provide } from 'vue'
export default {
setup(){
const name = '张三'
provide('name ', name ) //向下传递
return { name }
}
}
-------------------------------------------------------------
②子组件:
import { inject } from 'vue'
export default {
setup(){
const getName = inject( 'name'); //接收父传递下来的
return { getName }
}
}
vue3与vue2相比较3处修改 |
vue2 | vue3 | 详情 |
---|---|---|
beforcreate create | setup( ) | new Vue (开始→结束) |
beforeMounted Mounted | onBeforeMounted onMounted | 模板、DOM渲染 (开始→结束) |
beforeUpdate update | onBeforeUpdate onUpdate | 数据发生更新 (开始→结束) |
beforeDestory destory | onBeforeUnmount onUnmounted | 离开当前组件 (开始→结束) |
ref 对象 (一般用于基本数据类型 )
......
<template>
<p> 姓名 : {{ name }} </p>
</template>
......
setup(){
const name = ref('张三')
name.value = '李四';
return {name}
}
- ref('123') => 采用 Object.defineProperty() 进行响应
- ref( {name:'123'} ) => reactive( value:{ name : 123} ) => 采用 proxy 进行响应
reactive 对象 (一般用于引用数据类型 )
错误写法:(点击按钮,执行方法后 ,页面数据无法响应)
let arr = reactive([1,2]);
function changeArr(){
arr = ['3','4','5'];
}
正确写法:
let arr = reactive({ val:[1, 2] });
function changeArr(){
arr.val = ['3', '4', '5'];
}
const proxyObj = new Proxy({ name:'123' }, {
get:(obj,key,receiver)=>{
return Reflect.get(obj, key,receiver)
},
set:(obj,key,value,receiver) => {
return Reflect.set(obj, key, value, receiver)
}
})
详细了解vue2和vue3数据响应的区别 => 点击这里
区别 | watch (监听) | computed(计算属性) |
---|---|---|
资源消耗 | 不走缓存 (消耗资源大) | 默认走缓存 (消耗资源小) |
异步 | 支持异步 | 不支持异步 |
监听 | 一对多 (或一对一) | 多对一 (或一对一) |
import {watch, computed, ref} from 'vue'
......
setup(){
const num = ref (1);
watch(num,(newValue,oldValue)=>{ console.log(newValue); }); //num 改变就会执行
//-------------------------------------------------------------------------
const a = ref(2);
const b = ref(2);
let S = computed(()=>{return a.value * b.value }}); //a,b 一个改变就 重新计算 S
}
...
watchEffect(()=>{ console.log(a,b)); }) // 只监听ref 和 reactive 且出现在回调函数中的对象
...
import { onMounted , reactive} from 'vue';
实用场景 => 树形菜单 、流程图 、层级类组件 …
以树形组件为例 :
<tree-list :Arr="Arr" />
...
const Arr = [
{
text: "菜单1",
children: [
{
text: "菜单1-1",
children: [{ text: "菜单1-1-1" }, { text: "菜单1-1-2" }],
},
],
},
{ id: "2", text: "菜单2" },
];
2、编辑这个组件 => ( 通过 )
<div v-for="item in Arr">
<p>{{ item.text }}</p>
<tree-list
v-if="item.children"
:Arr="item.children"
:index="index"
:key="item.id"
/>
</div>
...
export default {
name: "tree-list",
props: {
Arr: { type: Array }
},
}
详细的样式、代码 请参考 => 点击这里
普通插槽 | 具名插槽 | 作用域插槽 |
---|---|---|
slot 标签接收,默认插入的内容 多写该标签只会重复插入 | v-slot =‘aa’ 可简写为 #aa 插入位置和slot的name一 一对应 | 通过props 传入 参数达到控制模板的目的 |
<template>
<div id="box">
<p> 标题 p>
<button @click='appendContent'> 插入内容 button>
<p> 内容:<span id='content'> span> <p/>
div>
<Teleport to="#content" :disabled = "bool">
teleport 插入到 改标签 中.
Teleport>
template>
<script>
...
const bool = ref(false);
const appendContent = ()=>{ bool.value = true }
return { bool, appendContent }
script>
①.下载和导入
npm i vuex --save
---------------------------
import Vuex from 'vuex'
Vue.use(Vuex);
②.创建store仓库: /store/index.js
import { createStore } from 'vuex';
export default createStore({
state: {name: 123 },
mutations:{ getname(state,newVal){this.state.name=newVal;} },
//同步方法:(只有mutations才能改state的值)
actions:{ getnameAsync(){ ... } }, //异步方法
geeter:{}, //相当于计算属性
modules: {} //将vuex分块
})
③.简单使用
import {useStore} from 'vuex'
export default {
setup(){
const store = useStore();
console.log(store.state.name); //获取
store.commit("getname", 12345); //修改
store.dispatch("getnameAsync", 12345); //修改
}
}
④.需要了解与 vue2 的差别 => 点击这里
pina | vuex |
---|---|
pinia 同时支持vue2和vue3 | vue2要用vuex 3 版本 vue3要用vuex 4 版本 |
不分同步异步,更好的ts支持 | 分同步异步,不太兼容ts |
享受自动补全 … | 需要注入,导入函数,调用他们 … |
① 安装
npm install pinia
②在 main.js 中 加入
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia' //导入pinia
const pinia = createPinia(); //调用创建pinia
createApp(App)
.use(pinia)
.mount('#app')
③去创建 pinia 仓库
一般选在 /src下的 store 文件夹下 例:创建为 pinia.js
import { defineStore } from 'pinia'
export const PiniaStore = defineStore('main',{ //导出 pinia仓库
state:() => { //相当于全局的 data()
return {
name:'张三',
age:18
}
},
getters:{}, //相当于全局的computed
actions:{} //相当于全局methods
})
③使用 (非常容易)
以/src/view/index.vue 为例:
<template>
<h3>{{pinia.name}}</h3> <!--使用-->
<h3>{{pinia.age}}</h3>
<button @click="pinia.age++">修改pinia数据</button> <!--修改-->
</template>
<script setup>
import { PiniaStore } from '../../store/pinia'
const pinia = PiniaStore();
</script>
创建路由
import { createRouter, createWebHistory } from 'vue-router'
const routerHistory = createWebHistory()
const router = createRouter({
history: routerHistory,
routes: []
})
export default router
import toMore from './components/toMore '
createApp(App)
.use(toMore)
.mount('#app')
let toMore= () => {
install(app) {
const com = import.meta.globEager("./*.vue");
for (const path in com ) {
app.component( com[path].name, com[path].default);
}}
};
export default toMore;
value
的变化 ,如果value 是对象下面还有层数则不监听;第一层
的变化。triggerRef
方法 去多次修改后去更新一次页面 如 triggerRe(obj)