天命有余畏,祖宗有余法。 ——王安石
前言
本文并非题目党,而是实实在在的硬核文章,如果有想要学习Vue3的网友,能够大抵的浏览一下本文,总体来说本篇博客涵盖了Vue3中绝大部分内容,蕴含罕用的CompositionAPI(组合式API)、其它CompositionAPI以及一些新的个性:Fragment、Teleport、Suspense、provide和inject。
我的项目搭建
既然是学习Vue3,那么首先应该须要的是如何初始化我的项目,在这里提供了两种形式供大家参考
- 形式一:vue-cli脚手架初始化Vue3我的项目
官网文档:https://cli.vuejs.org/zh/guid...
// 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上vue --version// 装置或者降级你的@vue/clinpm install -g @vue/cli// 创立vue create vue_test// 启动cd vue_testnpm run serve
- 形式二:vite初始化Vue3我的项目
vite官网:https://vitejs.cn/
// 创立工程npm init vite-app <project-name>// 进入工程目录cd <project-name>// 装置依赖npm install// 运行npm run dev
我的项目目录构造剖析
这里的我的项目目录构造剖析次要是main.js文件
Vue2外面的main.js
new Vue({el: '#app',components: {},template: ''});
Vue3外面的main.js
import { createApp } from 'vue'import App from './App.vue'createApp(App).mount('#app')
在Vue2外面,通过new Vue({})构造函数创立利用实例对象,而Vue3引入的不再是Vue的构造函数,引入的是一个名为createApp的工厂函数创立利用实例对象。
Vue3-devtool获取
devtool:https://chrome.zzzmh.cn/info?...
Composition API
setup
- 了解:Vue3.0中一个新的配置项,值为一个函数
- setup是所有Composition API(组合式API)的入口
- 组件中所用到的数据、办法等等,均要配置在setup外面
setup函数的两种返回值
- 若返回一个对象,则对象中的属性、办法,在模板中均能够间接应用
- 若返回一个渲染函数,则能够自定义渲染内容
setup的执行机会
- 在beforeCreate之前执行一次,此时this为undefined
setup的参数
props:值为对象,蕴含:组件内部传递过去,且组件外部申明接管了的属性
context:上下文对象
- attrs:值为对象,蕴含:组件内部传递过去,但没有在props配置中申明的属性,相当于this.$attrs
- slots:收到的插槽内容,相当于this.$slots
- emit:散发自定义事件的函数,相当于this.$emit
注意事项:
尽量不要与Vue2x的配置应用
- Vue2x的配置(data、methods、computed)均能够拜访到setup中的属性、办法
- setup中不能拜访Vue2x的配置(data、methods、computed)
- 如果data外面的属性和setup外面的属性有重名,则setup优先
- setup不能是一个async函数,因为返回值不再是return的对象,而是Promise,模板看不到return对象中的属性,然而前期也能够返回一个Promise实例,须要Suspense和异步组件的配合
示例一:setup函数的两种返回值
<template> <h2>练习setup相干内容</h2> <!--<h2>setup返回一个对象,并应用对象中的属性和办法</h2>--> <!--<p>姓名:{{student.name}}</p>--> <!--<p>年龄:{{student.age}}</p>--> <!--<button @click="hello">点击查看控制台信息</button>--> <hr> <h2>setup返回一个函数</h2></template><script> import {h} from 'vue' export default { name: "setupComponent", setup(){ // 属性 let student={ name:'张三', age:18, } // 办法 function hello() { console.log(`大家好,我叫${student.name},往年${student.age}`) } return{ // 返回一个对象 student, hello, } // return()=>h('h1','你好') // 返回一个函数 } }</script><style scoped></style>
这里须要留神的是setup外面定义的属性和办法均要return进来,否则无奈应用
示例二:setup外面的参数和办法和配置项混合应用
<template> <h2>setup和配置项混用</h2> <h2>姓名:{{name}}</h2> <h2>年龄:{{age}}</h2> <h2>性别:{{sex}}</h2> <button @click="sayHello">sayHello(Vue3外面的办法)</button> <button @click="sayWelcome">sayWelcome(Vue2外面的办法)</button></template><script> export default { name: "setup01_component", data(){ return{ sex:'男', sum:0, } }, methods:{ sayWelcome(){ console.log(`sayWelcome`) }, }, setup(){ let sum=100; let name='张三'; let age=18; function sayHello() { console.log(`我叫${name},往年${age}`) } return{ name, age, sayHello, test02, sum } } }</script><style scoped></style>
这段代码是先实现了setup外面的属性和办法,以及Vue2中配置项外面的属性和办法。接下来增加对应的混合办法
<template> <h2>setup和配置项混用</h2> <h2>姓名:{{name}}</h2> <h2>年龄:{{age}}</h2> <h2>性别:{{sex}}</h2> <button @click="sayHello">sayHello(Vue3外面的办法)</button> <button @click="sayWelcome">sayWelcome(Vue2外面的办法)</button> <br> <br> <button @click="test01">测试Vue2外面调用Vue3外面的属性和办法</button> <br> <br> <button @click="test02">测试Vue3setup外面调用Vue2外面的属性和办法</button> <br> <h2>sum的值是:{{sum}}</h2></template><script> export default { name: "setup01_component", data(){ return{ sex:'男', sum:0, } }, methods:{ sayWelcome(){ console.log(`sayWelcome`) }, test01(){ console.log(this.sex); // Vue2外面的属性(data外面的属性) // setup外面的属性 console.log(this.name); console.log(this.age); // setup外面的办法 this.sayHello(); } }, setup(){ let sum=100; let name='张三'; let age=18; function sayHello() { console.log(`我叫${name},往年${age}`) } function test02() { // setup外面的属性 console.log(name); console.log(age); // data外面的属性 console.log(this.sex); console.log(this.sayWelcome); } return{ name, age, sayHello, test02, sum } } }</script><style scoped></style>
这里新增了test01和test02办法,别离点击,控制台能够看到,点击配置项外面test01办法时,除了本身的sex属性有值,setup外面定义的属性也有值以及办法也能够调用,点击setup外面定义的test02办法时,控制台只能输入setup外面定义的属性和办法,而配置项外面定义的属性和办法值均为undefined。
- setup外面定义的属性和办法均能够在配置项外面应用(methods、computed、watch等),而配置项外面定义的属性和办法无奈在setup外面调用
- 如果setup外面的属性和data外面的属性有重名,则setup外面的属性优先
示例三:setup的执行机会
setup会在beforeCreate之前执行一次
<template> <h2>setup的执行机制</h2></template><script> export default { name: "setup_component03", setup(){ console.log('setup') }, beforeCreate(){ console.log('beforeCreate') } }</script><style scoped></style>
查看控制台的话咱们看到的程序是setup>beforeCreate
setup外面context和props的应用
Vue2外面props和slot的应用
解说setup这外面的两个参数之前,先回顾一下Vue2外面的相干常识
- props和自定义事件的应用
- attrs
- slot(插槽)
示例一:Vue2props和自定义事件的应用
筹备两个组件,别离为parent.vue组件和child.vue组件
parent.vue
<template> <div class="parent"> 我是父组件 <child msg="传递信息" name="张三" @sendParentMsg="getMsg"/> </div></template><script> import Child from "./Child"; export default { name: "Parent", components: {Child}, methods:{ getMsg(msg){ console.log(msg) } } }</script><style scoped> .parent{ padding: 10px; background-color: red; }</style>
child.vue
<template> <div class="child"> <h2>我是子组件</h2> <p>父组件传递过去的音讯是:{{msg}}</p> <p>父组件传递过去的音讯是:{{name}}</p> <button @click="sendMsg">向父组件的传递信息</button> </div></template><script> export default { name: "Child", props:{ msg:{ type:String, default:'' }, name:{ type:String, default:'' } }, mounted(){ console.log(this); }, methods:{ sendMsg(){ this.$emit("sendParentMsg",'告诉父组件更新') } } }</script><style scoped> .child{ padding: 10px; background-color: orange; }</style>
child组件对应的代码如下:
<template> <div class="child"> <h2>我是子组件</h2> <!--<p>父组件传递过去的音讯是:{{msg}}</p>--> <!--<p>父组件传递过去的音讯是:{{name}}</p>--> <p>父组件传递过去的音讯是:{{$attrs.msg}}</p> <p>父组件传递过去的音讯是:{{$attrs.name}}</p> <button @click="sendMsg">向父组件的传递信息</button> </div></template><script> export default { name: "Child", // props:{ // msg:{ // type:String, // default:'' // }, // name:{ // type:String, // default:'' // } // }, mounted(){ console.log(this); }, methods:{ sendMsg(){ this.$emit("sendParentMsg",'告诉父组件更新') } } }</script><style scoped> .child{ padding: 10px; background-color: orange; }</style>
子组件通过props接管父组件传递的信息,通过this.$emit()自定义事件向父组件传递信息。当应用props接收数据的时候,attrs外面的数据为空,如果没有应用props接收数据的话,那么props外面就有值。
示例二:Vue2外面slot的应用
同理筹备两个组件,一个Index.vue组件,另一个为MySlot.vue组件
Index.vue
<template> <div class="index"> <h2>我是Index组件</h2> <!--写法一--> <my-slot> <!--插槽外面的内容--> <h2>传入的slot参数</h2> <h2>传入的slot参数</h2> <h2>传入的slot参数</h2> <h2>传入的slot参数</h2> </my-slot> <!--写法二--> <my-slot> <template slot="header"> <h2>我是header组件</h2> </template> <template slot="footer"> <h2>我是footer附件</h2> </template> </my-slot> </div></template><script> import MySlot from "./MySlot"; export default { name: "Index", components: {MySlot} }</script><style scoped> .index{ padding: 10px; background: red; }</style>
MySlot.vue
<template> <div class="slot"> <h2>我是MySlot组件</h2> <slot></slot> <br> <slot name="header"></slot> <br> <slot name="footer"></slot> </div></template><script> export default { name: "MySlot", mounted(){ console.log(this); } }</script><style scoped> .slot{ padding: 10px; background: orange; }</style>
ref
- 作用:定义一个响应式数据
- 语法:const xxx=ref(initValue)
- 创立一个蕴含响应式数据的援用对象(reference对象);
- JS中操作数据:xxx.value=xxx;
模板中读取数据:不须要.value,间接:<div>{{xxx}}</div>
备注:
- 接管的数据能够是:根本类型,也能够是对象类型
- 根本类型的数据:响应式仍然是靠Object.defineProperty()的get和set实现的
对象类型的数据:外部求助了Vue3.0中的一个新函数-reactive函数
示例<template><h1>ref</h1><h2>ref定义根本数据类型</h2><p>姓名:{{name}}</p><p>年龄:{{age}}</p><p>婚否:{{isMarry}}</p><h2>ref定义对象类型</h2><p>喜好:{{hobby}}</p><p>证件类型:{{user.idCard}}</p><p>国籍:{{user.nation}}</p><button @click="changeName">批改信息</button></template><script>import {ref} from 'vue'export default {name: "refComponent",setup(){ // 应用根本数据类型 number,string,boolean, let name=ref('张三'); let age=ref(18); let isMarry=ref(false); // 应用ref定义数组 let hobby=ref(['吃饭','睡觉','打豆豆']); // 应用ref定义对象 let user=ref({ idCard:'身份证', nation:['中国','美国','英国','俄罗斯'] }) function changeName() { // 批改根本数据数据类型 name.value='李四'; // ref定义的响应式数据批改数据时必须要.value age.value=20; isMarry.value=true; // 批改对象数据类型 hobby.value[0]='玩游戏'; user.value.idCard='港澳台居民身份证'; user.value.nation[0]='挪威'; } return{ name, age, isMarry, changeName, user, hobby }}}</script><style scoped></style>
留神:
- ref定义的响应式数据批改数据时必须要.value
- ref定义的对象数据类型,外部求助了Vue3.0中的一个新函数-reactive函数
- 模板中应用数据时不须要.value
reactive函数
- 作用:定义一个对象类型的响应式数据(根本类型别用它,用ref函数)
- 语法:const 代理对象=reactive(被代理的对象)接管一个对象(或数组),返回一个代理器对象(Proxy的实例对象,简称Proxy对象)
- reactive定义的响应式数据是深层次的
外部基于ES6的Proxy实现,通过代理对象的操作源对象的外部数据都是响应式的
<template><h2>reactive响应式数据</h2><p>姓名:{{student.name}}</p><p>年龄:{{student.age}}</p><p>喜好:{{student.hobbies}}</p><button @click="changeStuInfo">扭转学生信息</button></template><script>import {reactive} from 'vue'export default { name: "reactiveComponent", setup(){ // 数据 let student=reactive({ name:'张三', age:19, hobbies:['吃饭','睡觉','打豆豆'] }); console.log(student) // 办法 function changeStuInfo() { student.name='李四'; student.age=20; student.hobbies[0]='做家务' } return{ student, changeStuInfo, } }}</script><style scoped></style>
reactive比照ref
从定义数据的角度比照
- ref用来定义:根本类型数据
- reactive用来定义:对象(或数组)类型数据
- 备注:ref也能够用来定义对象(或数组)类型的数据,它外部会主动通过reactive转为代理对象
从原理角度比照
- ref通过Object.defineProperty()的get和set实现(响应式)数据劫持
- reactive通过应用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象外部的数据
从应用角度
- ref定义的数据:操作数据须要.value,读取数据时模板中间接读取不须要.value
- reactive定义的数据:操作数据与读取数据均不须要.value
watch和watchEffect
// attr示意须要监督的属性 // 状况一:监督单个ref定义的响应式数据 watch(attr,(newValue,oldValue)=>{ console.log('attr变动了',newValue,oldValue); }) // 状况二; 监督多个ref定义的响应式数据 watch([attr1,attr2,....,attrn],(newValue,oldValue)=>{ console.log('attr1或attrn变动了',newValue,oldValue); }) // obj示意须要监听的对象 // 状况三:监督reactive定义的响应式数据 // 若watch监督的是reactive定义的响应式数据,则无奈正确取得oldValue // 若watch监督的是reactive定义的响应式数据,则强制关上开启了深度监督 watch(obj,(newValue,oldValue)=>{ console.log('obj变动了',newValue,oldValue) },{immediate:true,deep:false}); // 此处deep配置不在见效 // 状况四,监督reactive定义的响应式数据中的某个属性 watch(()=>person.job,(newValue,oldValue)=>{ console.log('person的job变动了',newValue,oldValue) },{immediate:true,deep:true}) // 状况五:监督reactive定义的响应式数据中的某一些属性 watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{ console.log('person的job变动了',newValue,oldValue) }) // 非凡状况 watch(()=>person.job,(newValue,oldValue)=>{ console.log('person的job变动了',newValue,oldValue) },{deep:false});// 此处因为是监督reactive所定义的对象中的某个属性,所以deep配置无效
watch
- 监督reactive定义的响应式数据时:oldValue无奈正确获取、强制开启了深度监督(deep配置生效)
监督reactive定义的响应式数据中某个属性时deep配置无效
示例一:wath监听ref定义的响应式数据<template><h2>watch监听ref定义的响应式数据</h2><h2>姓名:{{userName}}</h2><h2>年龄:{{age}}</h2><button @click="userName+='!'">批改姓名</button><button @click="age++">批改年龄</button><hr><h2>姓名:{{user.name}}</h2><h2>年龄:{{user.age}}</h2><button @click="user.name+='!'">批改姓名</button><button @click="user.age++">批改年龄</button></template><script>import {ref,watch} from 'vue';export default {name: "watch_component01",setup(){ let userName=ref('张三'); let age=ref(18); let user=ref({ name:'张三', age:21, }) // watch监听ref定义的单个响应式数据 watch(userName,(newValue,oldValue)=>{ console.log(`userName产生了变动,新值是:${newValue},旧值是:${oldValue}`) }); watch(age,(newValue,oldValue)=>{ console.log(`age产生了变动,新值是:${newValue},旧值是:${oldValue}`); }); // 如果须要监听多个ref定义的响应式数据的话,代码如下 /** * newValue和oldValue都是数组的模式,其中数组的第n位示意监听地位的第n位 * 例如:此例子中,监听属性的第一位是userName,那位newValue和oldValue对应的第一位也是 * userName。 * 如果有立刻执行,那么最开始的值为[],而不是[undefined,undefined] */ watch([userName,age],(newValue,oldValue)=>{ console.log('userName或age中的其中一个产生了变动,',newValue,oldValue) }) // watch监督ref定义的响应式对象数据 watch(user.value,(newValue,oldValue)=>{ console.log('person产生了变动',newValue,oldValue) }) watch(user,(newValue,oldValue)=>{ console.log('person产生了变动',newValue,oldValue); },{deep:false}) return{ userName, age, user }}}</script><style scoped></style>
示例二:watch监听reactive定义的响应式数据
<template><h1>watch监听reactive定义的响应式数据</h1><p>姓名:{{user.name}}</p><p>年龄:{{user.age}}</p><p>薪水:{{user.job.salary}}K</p><button @click="user.name+='!'">扭转姓名</button><button @click="user.age++">扭转年龄</button><button @click="user.job.salary++">扭转薪水</button></template><script>import {watch,reactive} from 'vue'export default {name: "watch_component02",setup(){ let user=reactive({ name:'张三', age:18, job:{ salary:20 } }); // 状况一:监听reactive定义的响应式数据,无奈正确获取oldValue /** * 此时的newValue和oldValue都是最新的数据 * 默认强制开启深度监督,此时深度监督生效 */ watch(user,(newValue,oldValue)=>{ console.log(newValue,oldValue); },{deep:false}); // 状况二,监督reactive定义的响应式数据的单个属性 // watch(()=>user.name,(newValue,oldValue)=>{ // console.log('name产生了变动',newValue,oldValue); // }); // watch(()=>user.age,(newValue,oldValue)=>{ // console.log('age产生了变动',newValue,oldValue); // }) // 状况三:监督reactive定义的响应式数据的多个属性 /** * newValue和oldValue都是数组的模式,其中数组的第n位示意监听地位的第n位 * 例如:此例子中,监听属性的第一位是userName,那位newValue和oldValue对应的第一位也是 * userName, */ // watch([()=>user.name,()=>user.age],(newValue,oldValue)=>{ // 写法一 // console.log('name或age中的某个属性产生了变动',newValue,oldValue); // }) // watch(()=>[user.name,user.age],(newValue,oldValue)=>{ // 写法二 // console.log('name或者age中的某个属性产生了变动',newValue,oldValue) // }) // 状况四:监督reactive定义的响应式数据的对象的某个属性,此时deep无效 /** * 留神:此时须要区别是reactive定义的对象还是reactive定义的对象外面的某个属性 * 此时deep无效,敞开了监督 */ // watch(()=>user.job,(newValue,oldValue)=>{ // console.log(newValue,oldValue); // },{deep:false}); return{ user }}}</script><style scoped></style>
watchEffect
- watch的套路是:既要指明监督的属性,也要指明监督的回调
- watchEffect的套路是:不必指明监督那个属性,监督的回调中用到那个属性,那就监督那个属性
watchEffect有点像computed
- 但computed重视的是计算出来的值(回调函数的返回值),所以必须要写返回值
而watchEffect更重视的是过程(回调函数的函数体),所以不必写返回值
示例<template><h1>watchEffect监督ref和reactive定义的响应式数据</h1><h2>以后求和:{{sum}}</h2><button @click="sum++">点我加1</button><hr><h2>以后的信息:{{msg}}</h2><button @click="msg+='!'">批改信息</button><hr><h2>姓名:{{person.name}}</h2><h2>年龄:{{person.age}}</h2><h2>薪资:{{person.job.j1.salary}}</h2><button @click="person.name+='!'">批改姓名</button><button @click="person.age++">批改年龄</button><button @click="person.job.j1.salary++">涨薪</button></template><script>import {ref,reactive,watchEffect} from 'vue';export default {name: "watch_effect_component01",setup(){let sum=ref(0);let msg=ref('你好');let person=reactive({ name:'张三', age:18, job:{ j1:{ salary:100, } }});/*** 在watchEffect外面写须要监督的属性,默认会执行一次* 如果是监督ref定义的响应式书则须要.value* 如果是监督reactive定义的响应式数据则间接监督*/watchEffect(()=>{ let x1=sum.value; let x2=person.job.j1.salary; console.log('watchEffect所指定的回调函数执行了');})return{ sum, msg, person}}}</script><style scoped></style>
Vue2响应式原理VSVue3响应式原理
在Vue2中次要是通过数据劫持来实现响应式原理的,也就是基于Object.defineProperty来实现的,而Vue3中是通过Proxy和Reflect来实现的(集体最低档次上的了解,还望各位大佬海涵)。
那么咱们来比照一下Vue3实现响应式的原理比Vue2实现响应式的原理好在哪里?
Vue2响应式原理
实现原理
- 对象类型:通过Object.defineProperty()对属性的读取,批改进行拦挡(数据劫持)
- 数组类型:通过重写更新数组的的一系列办法来实现拦挡。(对数组的变更办法进行了包裹)
Object.defineProperty(data,'count',{ get(){} set(){}})
存在问题
- 新增属性、删除属性、界面不会自动更新
- 间接通过下标批改数组,界面不会自动更新
先看一个简略的示例
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><div id="app"> <h1>学生信息</h1> <h4>姓名:{{student.name}}</h4> <h4>年龄:{{student.age}}</h4> <h4 v-if="student.sex">性别:{{student.sex}}</h4> <h4>喜好:{{student.hobbies}}</h4> <button @click="addSex">新增性别</button> <button @click="deleteAge">删除年龄</button> <button @click="updateHobbies">批改喜好</button></div><script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script><script> let vm=new Vue({ el:'#app', data(){ return{ student:{ name:'张三', age:18, hobbies:['吃饭','睡觉','打豆豆'] } } }, methods:{ addSex(){ // 新增性别 this.student.sex='male'; console.log(this.student); }, deleteAge(){ // 删除年龄 delete this.student.age; console.log(this.student); }, updateHobbies(){ // 批改喜好 this.student.hobbies[0]='玩游戏'; console.log(this.student); } } })</script></body></html>
别离调用按钮对应的办法,控制台能够看到,新增性别属性时,student外面有性别这个属性,然而并没有实现响应式(视图没有更新),同理,其它两个按钮对应的办法也是一样。
起因:Vue2.0想要实现响应式数据的话,必须先在data外面定义,之后从新增加的数据无奈实现响应式。
解决方案:
新增/批改:vue.$set(target,propName/index,value)
- {Object | Array} target
- {string | number} propertyName/index
- {any} value
删除:vue.$delete(target,propName/index)
- {Object | Array} target
- {string | number} propertyName/index
此时咱们批改对应的代码如下
addSex(){ // 新增性别 // this.student.sex='male'; // vm.$set(this.student,'sex','male'); this.$set(this.student,'sex',male); console.log(this.student); }, deleteAge(){ // 删除年龄 // delete this.student.age; // this.$delete(this.student,'age'); vm.$delete(this.student,'age'); console.log(this.student); }, updateHobbies(){ // 批改喜好 // this.student.hobbies[0]='玩游戏'; // this.$set(this.student.hobbies,0,'玩游戏'); // vm.$set(this.student.hobbies,0,'玩游戏'); /** * 或者应用数组变异的办法 * push() * pop() * shift() * unshift() * splice() * sort() * reverse() */ this.student.hobbies.splice(0,1,'玩游戏'); console.log(this.student); }
弊病
- 必须定义在data外面的数据能力实现响应式
- 如果前面增加的数据想要实现响应式,那么就须要调用对应的API
Object.defineProperty的简略示例
let student={ name:'张三', age:18, } let p={} Object.defineProperty(p,'name',{ get(){ // 读取name时触发 console.log('读取了name属性'); return student.name; }, set(value){ // 批改name时触发 console.log('name产生了变动,视图产生了变动'); student.name=value; } }); console.log(p.name); p.name='李四'; p.sex='male'; delete p.name;
Vue3响应式原理
对于Proxy和Reflect的用法这里不过多介绍,如果有想要理解的举荐看MDN或者阮一峰老师的ES6
- https://es6.ruanyifeng.com/
- https://developer.mozilla.org...
示例
let user={ name:'张三', age:18, } let p=new Proxy(user,{ get(target,propName){ console.log(`读取了p外面的${propName}属性`); Reflect.get(target,propName); // return target[propName]; }, set(target,propName,value){ console.log(`批改了p外面的${propName}属性`); // target[propName]=value; Reflect.set(target,propName,value); }, deleteProperty(target,propName){ console.log(`删除了p外面的${propName}属性`); // delete target[propName]; Reflect.deleteProperty(target,propName); } }); console.log(p.name); p.name='李四'; p.sex='male'; delete p.age;
查看控制台,当读取name属性的时候触发get()办法,新增或者批改属性的时候触发set()办法,删除的时候触发deleteProperty()办法,这就是Vue3.0对响应式的改良。
生命周期和钩子函数
Vue2.0生命周期和钩子函数
vue3.0生命周期和钩子函数
Vue2和Vue3生命周期比照 | ||
---|---|---|
beforeCreate | Not needed* | |
created | Not needed* | |
beforeMount | onBeforeMount | |
mounted | onMounted | |
beforeUpdate | onBeforeUpdate | |
updated | onUpdated | |
beforeUnmount | onBeforeUnmount | |
unmounted | onUnmounted | |
activated | onActivated | |
deactivated | onDeactivated |
因为 setup是围绕 beforeCreate和 created生命周期钩子运行的,所以不须要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该间接在 setup函数中编写。
Vue3x中能够持续应用Vue2x中的生命周期钩子,但有两个被更名
- beforeDestory改名为beforeUnmout
destoryed改名为unmouted
<template><!--Vue3x生命周期和钩子函数--><h3>Vue3x生命周期和钩子函数</h3><h3>数字:{{num}}</h3><button @click="num++">点我加加</button></template><script>import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'export default { name: "lifeCycleComponent", setup(){ let num=ref(0); console.log("======setup========="); onBeforeUnmount(()=>{ console.log("======onBeforeUnmount========="); }); onMounted(()=>{ console.log("======onMounted========="); }); onBeforeUpdate(()=>{ console.log("======onBeforeUpdate========="); }); onUpdated(()=>{ console.log("======onUpdated========="); }); onBeforeUnmount(()=>{ console.log("======onBeforeUnmount========="); }) onUnmounted(()=>{ console.log("======onUnmounted========="); }); return{ num, } }}</script><style scoped></style>
自定义hook
- 什么是hook:实质是一个函数,把setup函数中应用的CompositionAPI进行了封装
- 相似于vue2x中mixin
- 自定义hook的劣势:复用代码,让setup中的逻辑更分明易懂
作为初学hook的我来说,我对于hook并没有理解多少,这里贴一些本人练习中的示例,现阶段的我感觉不进去export 导出函数和hook的区别和长处。示例的话是鼠标点击的时候获取以后坐标
示例
<template> <h2>自定义hook</h2> <h2>以后x的坐标:{{x}},以后y的坐标:{{y}}</h2></template><script> import {reactive,toRefs,onMounted,onUnmounted} from 'vue' export default { name: "hook_component01", setup(){ // 数据 let point=reactive({ x:0, y:0, }) // 办法 function getPoint(event){ console.log(event) point.x=event.clientX; point.y=event.clientY; } // 生命周期钩子函数 onMounted(()=>{ window.addEventListener('click',getPoint); }) onUnmounted(()=>{ window.removeEventListener('click',getPoint); }) return{ ...toRefs(point) } } }</script><style scoped></style>
抽离独自的hook
- 新建目录hook
- hook目录下新建文件usePoint.js
usePoint.js
import {reactive,onMounted,onUnmounted} from 'vue'export let getPoint=()=>{ // 数据 let point=reactive({ x:0, y:0, }) // 办法 function getPoint(event){ console.log(event) point.x=event.clientX; point.y=event.clientY; } // 生命周期钩子函数 onMounted(()=>{ window.addEventListener('click',getPoint); }) onUnmounted(()=>{ window.removeEventListener('click',getPoint); }) return point}
须要引入hook的.vue文件
import {reactive,toRefs,onMounted,onUnmounted} from 'vue' import {getPoint} from "./hook/usePoint"; export default { name: "hook_component01", setup(){ let point=getPoint(); return{ ...toRefs(point) } } }
这个就是最简略hook的用法,如果有晓得export 导出函数和hook区别的大佬,能够在下方评论区留言,感谢不敬!!!
其它Composition API
toRef与toRefs
toRef
- 作用:创立一个ref对象,其value值指向另一个对象中的某个属性值
- 语法:const name=toRef(obj,'name')
- 利用:要将响应式对象中的某个属性独自提供给内部应用时
- 扩大:toRefs与toRef性能统一,但能够批量创立多个ref对象,toRefs(obj)
示例一
<template> <h2>toRef与toRefs</h2> <h2>姓名:{{person.name}}</h2> <h2>年龄:{{person.age}}</h2> <h2>薪水:{{person.job.salary}}k</h2> <button @click="person.name+='!'">批改姓名</button> <button @click="person.age++">批改年龄</button> <button @click="person.job.salary++">涨点薪资</button></template><script> import {reactive} from 'vue' export default { name: "toRef_component", setup(){ let person=reactive({ name:'二郎神杨杨戬', age:18, job:{ salary:20 } }) return{ person, } } }</script><style scoped></style>
示例一外面间接返回person对象,导致每次取值的时候都须要person.xxx,这样既不美观也不优雅,批改一下代码。
示例二
<template> <h2>toRef与toRefs</h2> <h2>姓名:{{name}}</h2> <h2>年龄:{{age}}</h2> <h2>薪水:{{salary}}k</h2> <button @click="name+='!'">批改姓名</button> <button @click="age++">批改年龄</button> <button @click="salary++">涨点薪资</button></template><script> import {reactive,toRef} from 'vue' export default { name: "toRef_component", setup(){ let person=reactive({ name:'二郎神杨杨戬', age:18, job:{ salary:20 } }) return{ name:toRef(person,'name'), age:toRef(person,'age'), salary:toRef(person.job,'salary') } } }</script><style scoped></style>
谬误用法示例一
<template> <h2>toRef与toRefs</h2> <h2>姓名:{{name}}</h2> <h2>年龄:{{age}}</h2> <h2>薪水:{{salary}}k</h2> <button @click="name+='!'">批改姓名</button> <button @click="age++">批改年龄</button> <button @click="salary++">涨点薪资</button></template><script> import {reactive,toRef,toRefs} from 'vue' export default { name: "toRef_component", setup(){ let person=reactive({ name:'二郎神杨杨戬', age:18, job:{ salary:20 } }) return{ name:person.name, age:person.age, salary:person.job.salary } } }</script><style scoped></style>
谬误用法示例二
<template> <h2>toRef与toRefs</h2> <h2>姓名:{{name}}</h2> <h2>年龄:{{age}}</h2> <h2>薪水:{{salary}}k</h2> <h2>peron对象{{person}}</h2> <button @click="name+='!'">批改姓名</button> <button @click="age++">批改年龄</button> <button @click="salary++">涨点薪资</button></template><script> import {reactive,toRef,toRefs,ref} from 'vue' export default { name: "toRef_component", setup(){ let person=reactive({ name:'二郎神杨杨戬', age:18, job:{ salary:20 } }) return{ person, name:ref(person.name), age:ref(person.age), salary:ref(person.job.salary) } } }</script><style scoped></style>
toRefs
示例一
<template> <h2>toRef与toRefs</h2> <h2>姓名:{{name}}</h2> <h2>年龄:{{age}}</h2> <h2>薪水:{{job.salary}}k</h2> <button @click="name+='!'">批改姓名</button> <button @click="age++">批改年龄</button> <button @click="job.salary++">涨点薪资</button></template><script> import {reactive,toRef,toRefs} from 'vue' export default { name: "toRef_component", setup(){ let person=reactive({ name:'二郎神杨杨戬', age:18, job:{ salary:20 } }) return{ ...toRefs(person) } } }</script><style scoped></style>
shallowRef和shallowReactive
- shallowReactive:只解决对象最外层属性的响应式(浅响应式)
- shallowRef:只解决根本数据类型的响应式,不进行对象的响应式解决
什么时候用
- 如果有一个对象数据,构造比拟深,但变动时只是外层属性变动用shallowReactive
- 如果有一个对象数据,后续性能不会批改该对象中的属性,而是生新的对象来替换用shallowRef
shallowRef示例
<template> <h2>shallowRef示例</h2> <h2>以后sum的值是:{{sum}}</h2> <button @click="sum++">点我sum加1</button> <h2>以后x.y的值是:{{x.y}}</h2> <button @click="x.y++">点我x.y加1</button> <button @click="x={y:100}">点我替换y的值</button></template><script> import {shallowRef,ref} from 'vue' export default { name: "shallowRef_component", setup(){ let sum=ref(0); let x=shallowRef({ y:0, }) return{ sum, x, } } }</script><style scoped></style>
这里咱们通过ref和shallowRef进行比对,点击x.y加1按钮的时候,视图不会触发更新,因为y的值对象作为深层次的,而间接点击sum加1的按钮的时候能够触发更新,sum间接是浅层次的,替换y的值的时候替换的是整个x的值(即整个对象),而不是x外面的值进行操作。
shallowReactive示例
<template> <h2>shallowReactive示例</h2> <h2>姓名:{{name}}</h2> <h2>年龄:{{age}}</h2> <h2>薪资:{{job.salary}}k</h2> <button @click="name+='!'">批改姓名</button> <button @click="age++">批改年龄</button> <button @click="job.salary++">涨点薪资</button></template><script> import {shallowReactive,toRefs} from 'vue' export default { name: "shallowReactive01_component", setup(){ let person=shallowReactive({ name:'张三', age:18, job:{ salary:20, } }) return{ ...toRefs(person) } } }</script><style scoped></style>
点击批改姓名和批改年龄的按钮时,能够看到视图发生变化,点击涨薪的时候视图不会发生变化,然而数据产生了变动,这个大家能够应用控制台进行测试。
readonly和shallowReadonly
- readonly:让一个响应式数据变为只读的(深只读)
shallowReadonly:让一个响应式变为只读的(浅只读)
示例一<template><h2>readonly与shallowReadonly</h2><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><h2>薪资:{{job.salary}}</h2><h2>以后sum的值是:{{sum}}</h2><button @click="name+='!'">批改姓名</button><button @click="age++">批改年龄</button><button @click="job.salary++">涨薪(readonly)</button><button @click="job.salary++">涨薪(shallowReadonly)</button><button @click="sum++">点我加1</button></template><script>import {ref,reactive,readonly,shallowReadonly,toRefs} from 'vue'export default { name: "shallowReadonly_component", setup(){ let sum=ref(0); let person=reactive({ name:'二郎神杨戬', age:21, job:{ salary:200 } }); person=readonly(person); sum=readonly(sum); // person=shallowReadonly(person); // sum=readonly(sum); return{ sum, ...toRefs(person) } }}</script><style scoped></style>
应用readonly的时候,按钮点击全副生效,咱们看下shallowReadonly的成果
应用shallowReadonly的时候,批改姓名,批改年龄都不会发生变化,只有涨薪产生了变动
toRaw和markRaw
toRaw
- 作用:将一个由reactive生成的响应式对象转为一般对象
- 应用场景:用于读取响应式对象对应的一般对象,对这个一般对象的所有操作,不会引起页面更新
markRow
作用:标记一个对象,使其永远不会再成为响应式对象
利用场景:
- 有些值不应该被设置为响应式的,例如简单的第三方类库,
- 当渲染具备不可变的数据源的大列表时,跳过响应式转换能够进步性能
示例一
<template> <div style="width: 800px;margin: 0 auto"> <h2>toRaw与markRow</h2> <h2>姓名:{{name}}</h2> <h2>年龄:{{age}}</h2> <h2>薪资:{{job.salary}}k</h2> <button @click="name+='!'">批改姓名</button> <button @click="age++">批改年龄</button> <button @click="job.salary++">涨点薪资</button> <button @click="showRawPerson">输入最原始的person对象</button> </div></template><script> import {ref,reactive,toRaw,markRaw,toRefs} from 'vue' export default { name: "toRaw01_component", setup(){ let sum=ref(0); let person=reactive({ name:'二郎神杨戬', age:18, job:{ salary:20 } }) function showRawPerson() { let p=toRaw(person) console.log(p); let sum=toRaw(sum); console.log(sum); // 对ref定义的响应式数据有效 } return{ sum, ...toRefs(person), showRawPerson } } }</script><style scoped></style>
调用showRawPerson办法的时候,控制台能够看到输入最原始的person,sum的话,输入的undefined,toRaw对ref定义的响应式数据有效,接下来看下markRow的成果
示例二
<template> <div style="width: 800px;margin: 0 auto"> <h2>toRaw与markRow</h2> <h2>姓名:{{name}}</h2> <h2>年龄:{{age}}</h2> <h2>薪资:{{job.salary}}k</h2> <button @click="name+='!'">批改姓名</button> <button @click="age++">批改年龄</button> <button @click="job.salary++">涨点薪资</button> <button @click="showRawPerson">输入最原始的person对象</button> <h2>车的信息是:{{person.car}}</h2> <button @click="addCar">给人增加一辆车</button> <template v-if="person.car"> <button @click="person.car.name+='!'">批改车名</button> <button @click="person.car.price++">批改车的价格</button> <button @click="changeCarPrice">批改车的价格</button> </template> </div></template><script> import {ref,reactive,toRaw,markRaw,toRefs} from 'vue' export default { name: "toRaw01_component", setup(){ let sum=ref(0); let person=reactive({ name:'二郎神杨戬', age:18, job:{ salary:20 } }) function showRawPerson() { let p=toRaw(person) console.log(p); let sum=toRaw(sum); console.log(sum); // 对ref定义的响应式数据有效 } function addCar() { let car={name:'宝马',price:40} person.car=markRaw(car); } function changeCarPrice() { person.car.price++; console.log(person.car.price) } return{ sum, person, ...toRefs(person), showRawPerson, addCar, changeCarPrice } } }</script><style scoped></style>
这里新增了一个车信息的办法和相干属性到person对象外面,失常状况下,间接在reactive外面的追加的数据会实现响应式的,然而这里应用了markRaw办法,所以点击批改车的名字和价格时数据产生了变动,然而视图不会更新。
customRef
- 作用:创立一个自定义的ref,并对其依赖项跟踪和更新触发进行显示管制,它须要一个工厂函数,该函数接管 track 和 trigger 函数作为参数,并且应该返回一个带有 get 和 set 的对象。
实现防抖成果: - 1、先实现自定义双向绑定
2、实现了双向绑定之后,实现防抖
自定义双向绑定<template><h2>customRef示例</h2><input type="text" v-model="msg"><h2>{{msg}}</h2></template><script>import {customRef,} from 'vue'export default { name: "customRef01_component", setup(){ function myRef(msg){ // 自定义ref函数 return customRef((track,trigger)=>{ return{ get(){ console.log('读取了值') track(); return msg; }, set(newValue){ console.log(`批改了值,批改后的值是:${newValue}`); msg=newValue; trigger(); } } }) } let msg=myRef('你好'); return{ msg } }}</script><style scoped></style>
在这里咱们实现了数据的双向绑定,接下来是实现防抖
<template> <div style="width: 800px;margin: 0 auto"> <h2>customRef示例</h2> <input type="text" v-model="msg"> <h2>{{msg}}</h2> </div></template><script> import {customRef,} from 'vue' export default { name: "customRef01_component", setup(){ function myRef(msg,delay){ // 自定义ref函数 let timer; return customRef((track,trigger)=>{ return{ get(){ console.log('读取了值') track(); return msg; }, set(newValue){ timer=setTimeout(()=>{ console.log(`批改了值,批改后的值是:${newValue}`); msg=newValue; trigger(); },delay) } } }) } let msg=myRef('你好',500); return{ msg } } }</script><style scoped></style>
响应式数据的判断
- isRef:查看一个值是否为ref对象
- isReactive:查看一个对象是否由reactive创立的响应式代理
- isReadonly:查看一个对象是否由readonly创立的只读代理
- isProxy:查看一个对象是否由reactive或者readonly办法创立的代理
provide和inject
- 作用:实现祖与后辈组件间通信
- 套路:父组件有一个provide选项来提供数据,后辈组件有一个inject选项来开始应用这些数据
祖组件
<template> <h2>我是祖组件</h2> <h3>汽车信息</h3> <p>名称:{{name}}</p> <p>价格:{{price}}</p> <inject_component></inject_component></template><script> import {reactive,toRefs,provide} from 'vue' export default { name: "provide_component", setup(){ let car=reactive({ name:'宝马', price:'40w' }); provide('car',car); // 提供provide return{ ...toRefs(car) } } }</script><style scoped></style>
后辈组件
<template> <h2>我是孙组件</h2> <h3>汽车信息</h3> <p>名称:{{name}}</p> <p>价格:{{price}}</p></template><script> import {inject,toRefs,ref} from 'vue' export default { name: "inject_component", setup(){ let car=inject("car"); //应用inject接管 return{ ...toRefs(car) } } }</script><style scoped></style>
Fragment
- 在vue2中:组件必须有一个根标签
- 在vue3中:组件能够没有根标签,外部会将多个根标签蕴含在一个Fragment虚构元素中
益处:缩小标签层级,缩小内存占用
Teleport
Teleport是一种可能将咱们的组件html构造挪动到指定地位的技术
<teleport to='挪动地位'> <div v-if='isShow' class='mark'> <div class="dialog"> <h3>我是一个弹窗</h3> <button @click='isShow=true'>敞开弹窗</button> </div> </div></teleport>
实现一个弹窗居中显示,有四个组件,别离为:teleport_parent,teleport_child,teleport_son,teleport_dialog,而后须要实现的成果是在teleport_son组件引入teleport_dialog组件,teleport_dialog显示的时候在屏幕正地方
teleport_parent.vue
<template> <div class="parent"> <h2>我是parent组件</h2> <teleport_child/> </div></template><script> import Teleport_child from "./teleport_child"; export default { name: "teleport_parent", components: {Teleport_child} }</script><style scoped> .parent{ background-color: red; padding: 10px; }</style>
teleport_child.vue
<template> <div class="child"> <h2>我是child组件</h2> <teleport_son/> </div></template><script> import Teleport_son from "./teleport_son"; export default { name: "teleport_child", components: {Teleport_son} }</script><style scoped> .child{ background-color: orange; padding: 10px; }</style>
teleport_son.vue
<template> <div class="son"> <h2>我是son组件</h2> <teleport_dialog/> </div></template><script> import Teleport_dialog from "./teleport_dialog"; export default { name: "teleport_son", components: {Teleport_dialog} }</script><style scoped> .son{ background-color: yellow; padding: 10px; }</style>
teleport_dialog.vue
<template> <div> <button @click="isShow=true">点我弹窗</button> <div class="dialog_container" v-if="isShow"> <h2>我是弹窗组件</h2> <div class="dialog_body"> <h2>我是内容</h2> <h2>我是内容</h2> <h2>我是内容</h2> <h2>我是内容</h2> </div> <button @click="isShow=false">敞开按钮</button> </div> </div></template><script> import {ref} from 'vue' export default { name: "teleport_dialog", setup(){ let isShow=ref(false); return{ isShow } } }</script><style scoped> .dialog_container{ width: 500px; height: 300px; background: red; }</style>
实现的成果如下
当咱们点击按钮的时候,成果是这样的
点击按钮的时候,弹窗显示,然而在son组件外面会扭转son的高度,这样子不太好看,如果弹窗应用定位的话能够使其脱离文档流,而不会撑开son外面的高度,然而postion:absolute是依据最近的有定位元素的父元素进行定位的,所以此办法不牢靠,咱们须要实现的成果是依据body来定位
批改dialog的款式
<template> <div> <button @click="isShow=true">点我弹窗</button> <teleport to="body"> <div class="mask" v-if="isShow"> <div class="dialog_container"> <h2>我是弹窗组件</h2> <div class="dialog_body"> <h2>我是内容</h2> <h2>我是内容</h2> <h2>我是内容</h2> <h2>我是内容</h2> </div> <button @click="isShow=false">敞开按钮</button> </div> </div> </teleport> </div></template><script> import {ref} from 'vue' export default { name: "teleport_dialog", setup(){ let isShow=ref(false); return{ isShow } } }</script><style scoped> .mask{ position: absolute; top: 0px; left: 0px; right: 0px; bottom: 0px; background: rgba(0,0,0,.5); } .dialog_container{ width: 500px; height: 300px; background: red; position: absolute; left: 50%; top: 50%; margin-left: -250px; margin-top: -150px }</style>
Suspense
作用:期待异步组件时渲染一些额定的内容,让利用有更好的用户体验
应用步骤:
- 异步引入组件
import {defineAsyncComponent} from 'vue'const child=defineAsyncComponent(()=>import('./components/Child.vue'))
- 应用Suspense包裹组件,并配置好default和fallback
<template> <div class="app"> <h3>我是app组件</h3> <Suspense> <template v-slot:default> <Child/> </template> <template v-slot:fallback> <h3>加载中......</h3> </template> </Suspense> </div></template>
示例
suspense.vue
<template> <div class="suspense"> <h2>我是suspense组件</h2> <child/> </div></template><script> import Child from "./child"; export default { name: "suspense01_component", components: {Child} }</script><style scoped> .suspense{ background-color: red; padding: 10px; }</style>
child.vue
<template> <div class="child"> <h2>我是child组件</h2> </div></template><script> export default { name: "child" }</script><style scoped> .child{ background-color: orange; padding: 10px; }</style>
如果应用以上代码引入的话,那么suspense和child组件将会同时加载,如果child外面还有其它子组件的话,子组件外面还有子组件,在网络慢的状况下,用户可能就看不到child组件以及child外面其它的组件。这会影响用户体验,批改对应的代码
suspense.vue
<template> <div class="suspense"> <h2>我是suspense组件</h2> <suspense> <template v-slot:default> <child/> </template> <template v-slot:fallback> <h3>请稍等,加载中。。。</h3> </template> </suspense> </div></template><script> // import Child from "./child"; // 动态引入 import {defineAsyncComponent} from 'vue' const Child=defineAsyncComponent(()=>import('./child')) // 异步引入 export default { name: "suspense01_component", components: {Child} }</script><style scoped> .suspense{ background-color: red; padding: 10px; }</style>
child.vue
<template> <div class="child"> <h2>我是child组件</h2> <h2>以后sum的值是:{{sum}}</h2> </div></template><script> import {ref} from 'vue' export default { name: "child", setup(){ let sum=ref(0); return new Promise((resole,reject)=>{ setTimeout(()=>{ resole({ sum }); },3000) }) } }</script><style scoped> .child{ background-color: orange; padding: 10px; }</style>
为了看到成果,提早了3秒之后显示组件
Vue2x和Vue3x的其它变动
1.全局API的转移
vue2.x有许多全局API和配置,例如:全局注册组件、注册全局指令等
- 注册全局组件
Vue.component('MyButton',{ data:()=>{ count:0, }, template:'<button @click="count++">clicked {{count}} times</button>' });
- 注册全局指令
Vue.directive('focus',{ inserted:el=>el.foucus})
- Vue3.0中对这些API做出了调整
将全局的API,即Vue.xxx调整到利用实例app上
2.x 全局API(Vue) | 3.x 实例API(app) |
---|---|
Vue.config.xxx | app.config.xxx |
Vue.config.productionTip | 移除 |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use |
Vue.prototype | app.config.globalProperties |
2.其它扭转
- data 选项应始终被申明为一个函数
- 适度类的写法
Vue2.x的写法
.v-enter.v-leave-to{ opacity:0,}v-leave,v-enter-to{ opacity:1}
Vue3.x的写法
.v-enter-from,.v-leave-to{ opacity:0}.v-leave-to,.v-enter-to{ opacity:1}
- 移除keyCode作为v-on的修饰符,同时也不再反对config.keyCodes
- 移除v-on.navtive修饰符
父组件绑定事件
<my-component> v-on:close="handleComponentEvent"v-on:click="handleNativeClickEvent"</my-component>
子组件中申明自定义组件
export default{ emits:['close']}
移除过滤器(filter)
过滤器尽管看起来不便,但它须要一个自定义语法,突破大括号内表达式'只是javascript'的假如,这不仅有学习老本,而且有实现成 本, 倡议用法调用或者计算属性去替换过滤器
Vue3x相干材料
相干库名称 | 在线地址 |
---|---|
Vue 3.0 官网文档(英文) | 在线地址 |
Vue 3.0 中文文档 | 在线地址 |
Composition-API手册 | 在线地址 |
Vue 3.0 源码学习 | 在线地址 |
Vue-Router 官网文档 | 在线地址 |
Vuex 4.0 | Github |
vue-devtools | Github |
Vite 源码学习 | 线上地址 |
Vite 2.0 中文文档 | 线上地址 |
Vue3 新动静 | 线上地址 |
Vue3xUI组件库
Element-plus
- 仓库地址:https://github.com/element-pl...
- 文档地址:https://element-plus.gitee.io...
开源我的项目
- Vue 3.0 + Vite 2.0 + Vue-Router 4.0 + Element-Plus + Echarts 5.0 + Axios 开发的后盾管理系统:https://github.com/newbee-ltd...
- Vue3.0+TypeScript+NodeJS+MySql编写的一套后盾管理系统:https://github.com/xiaoxian52...
Ant Design of Vue
- 仓库地址:https://github.com/vueCompone...
- 文档地址:https://antdv.com/docs/vue/in...
开源我的项目
- AntdV后盾管理系统:https://github.com/iczer/vue-...
- vue3.x + ant-design-vue(beta 版本,收费商用,反对 PC、平板、手机):https://github.com/chuzhixin/...
- 基于 Vue3.0 + Vite + Ant Design Vue:https://github.com/lirongtong...
Vant
- 仓库地址:https://github.com/youzan/vant
- 文档地址:https://vant-contrib.gitee.io...
开源我的项目
- newbee-mall Vue3 版本:https://github.com/newbee-ltd...
- 高仿微信笔记本:https://github.com/Nick930826...
- 仿京东淘宝电商:https://github.com/geekskai/v...
NutUI 3
- 仓库地址:https://github.com/jdf2e/nutui
文档地址:https://nutui.jd.com/#/index
Vue3X相干视频
相干库名称 | 在线地址 |
---|---|
Vue 3.0 实战星座物语 H5 我的项目 | 在线地址 |
Vue 3.0 UI 组件库开发 | 在线地址 |
Vue 3.0 + Vite 手册浏览 | 在线地址 |
Vue 3.0 入门之我的项目搭建(杨村长) | 在线地址 |
Vue 3.0 入门(技术胖)【不太倡议举荐】 | 在线地址 |
Vite 2.0 插件开发指南 | 在线地址 |
Vue 3.0 + Vite 2.0 疾速搭建 Electron 利用 | 在线地址 |
Vue3.0视频(强烈推荐) | 在线地址 |
Vue3.0驾照题库 | 在线地址 |
参考资料
掘金地址:https://juejin.cn/post/695512...
Vue.js官网:https://v3.cn.vuejs.org/
bilibili地址:https://www.bilibili.com/vide...
MDN地址:https://developer.mozilla.org...
ES6地址:https://es6.ruanyifeng.com/
总结
总体来说,学完这篇博客根本涵盖了Vue3中的绝大部分内容,写的也特地具体,笔记,配套材料和实战视频都有,次要看你愿不愿意抽出工夫去学习,我本人认为学习是一辈子的事件,当然学习也是一件特地孤单的事件。如果本篇文章对您有所帮忙的话,记得点赞、珍藏和关注,原创不易,三连反对,感激大家!