Vue3
Vue3介绍
Vue.JS 3.0兼容2.0的大多数语法。
性能提升
- 项目打包体积更小;
- 需要的运行内存更小;
- 初次渲染更快,更新渲染更快;
- 使用
Proxy
代替Object.defineProperty
实现数据响应式;
- 重写虚拟
Dom
的实现和Tree-Shaking
;(提升模板编译的速度;摇树操作–>减少项目打包的体积)
- 更好的支持
TypeScript
;
获取Vue3.0
- cdn方式
- 创建实例对象的方式不一样;
- 挂载应用实例的方式不一样;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="apps"> <h2>{{msg}}</h2> </div> </body>
<script src="./vue.global.js"></script> <script> /** vue2: 创建实例对象的方式 new一个类 new Vue({ el:"#apps", data:{ }, methods:{ } }) */
// /** 调用Vue的createApp()静态方法: Vue.createApp(); */ const app = Vue.createApp({ // data必须是个函数 data(){ return{ msg:"hello Vue3" } } });
// 挂载应用实例 app.mount("#apps"); </script> </html>
|
现象:
- 通过
app.mount("#apps")
挂载应用实例,浏览器会展现出经h2
渲染后的hello Vue3
;
- 通过
el:"#apps"
方式挂载应用实例,浏览器会展现出经h2
渲染后的{{msg}}
;即挂载点挂载失败了;
vue cli脚手架
- 安装最新版本的
vue cli
脚手架:npm i @vue/cli -g
;
- 创建项目:
vue create 项目名称
- 选择vue3版本的默认选项
- 现象:目录结构3与2大致一样;语法部分不一样;例如:
main.js(入口文件):
1 2 3 4 5
| import { createApp } from 'vue' import App from './App.vue'
createApp(App).mount('#app')
|
1 2 3 4 5 6 7 8 9
| import Vue from 'vue' import App from './App.vue'
Vue.config.productionTip = false
new Vue({ render: h => h(App), }).$mount('#app')
|
Vite构建工具
Vite是一个web开发构建工具
Vite仅支持vue3.0+的项目,也即是说我们无法在其中使用vue2.x
在成功安装 vue-cli 之后即可使用 Vue3 带来的新工具 Vite 构建项目
Vite的底层不是基于webpack
Vite的底层是基于浏览器对原生ES模块语法的支持来进行项目构建,所以搭建速度很快
- 创建项目
1
| npm init vite@latest 项目名称
|
安装项目依赖
在项目的根目录下运行命令
1 2 3 4 5
| cd 项目名称 npm install
npm i
|
启动项目
在项目的根目录下运行命令
- 默认监听端口号为3000
新增特性
Fragment模板碎片
- vue2中组件的模板必须有一个唯一的根标签
- vue3中组件模板可以有多个根标签(可能编辑器会报错,是因为
Vetur
插件的问题,看下面的第二个参考文献来解决)
在Vite创建的项目中 src–>components下新建一个home.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <template> <!-- 在vue2 中 template里面只能有唯一的一个根标签,一般设置div,在div里面写代码 --> <!-- 在vue3中,可以有多个根标签,当然也可以套个div作为唯一根标签 --> <h2>{{msg}}</h2> <h2>{{texts}}</h2> </template>
<script> export default { data() { return { msg: "hello vue3", texts: "你好 vue3" }; } }; </script>
<style> </style>
|
在App.vue里面进行 导入,注册,调用 三部曲
在vue3 里面 script添加了setup属性,有这个属性则可以不需要手动注册子组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <script setup>
import Home from './components/home.vue'
</script>
<template>
<Home/> </template>
|
Composition(组合)API
- 作用:将不同根组件的相同部分抽离出来,单独维护,提高代码的复用率
- setup()方法
- 在组件渲染完成之前自动执行,所以不能在setup中通过this访问组件对象
- 组件相同的代码块放到setup里面,让组件在第一次创建的时候执行里面的逻辑,完成对应的操作;
- 在setup方法中返回的数据,会自动和组件data中的数据进行合并;
- 在setup方法中返回的方法,会自动和methods对象进行合并;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <template> <!-- 在vue2 中 template里面只能有唯一的一个根标签,一般设置div,在div里面写代码 --> <!-- 在vue3中,可以有多个根标签,当然也可以套个div作为唯一根标签 --> <h2>{{name}}</h2> <h2>{{age}}</h2> <hr> <button @click="getName">按钮</button> </template>
<script> export default { setup(){
return{ name:"张三", age:20, getName(){ console.log(this.name); } } }, data() { return { msg: "hello vue3", texts: "你好 vue3" }; } }; </script>
<style> </style>
|
现象:页面展示经h2
渲染后的张三
20
与一个按钮
,点击按钮
,控制台输出张三
那如果直接在setup方法里面进行修改data的操作呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <template> <!-- 在vue2 中 template里面只能有唯一的一个根标签,一般设置div,在div里面写代码 --> <!-- 在vue3中,可以有多个根标签,当然也可以套个div作为唯一根标签 --> <h2>{{name}}</h2> <h2>{{age}}</h2> <hr> <button @click="getName">按钮</button> </template>
<script> export default { setup(){
return{ name:"张三", age:20, getName(){ this.name="李四"; console.log(this.name); } } }, data() { return { msg: "hello vue3", texts: "你好 vue3" }; } }; </script>
<style> </style>
|
现象:点击按钮之后,页面上的值依旧是张三
,但是如果打印this.name
,输出的是李四
;这是因为setup方法里面的data并不是响应式数据
那如何实现响应式数据呢?
reactive
作用:帮助我们创建响应式数据对象
语法:
1 2 3 4 5 6 7 8 9
| import {reactive} from "vue"; export default{ setup(){ const obj = reactive({ }); return obj; } }
|
具体代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <template> <!-- 在vue2 中 template里面只能有唯一的一个根标签,一般设置div,在div里面写代码 --> <!-- 在vue3中,可以有多个根标签,当然也可以套个div作为唯一根标签 --> <h2>{{name}}</h2> <h2>{{age}}</h2> <hr> <button @click="getName">按钮</button> </template>
<script> import { reactive } from "vue"; export default { setup() {
const obj = reactive({ name: "张三", age: 20 });
return obj; }, data() { return { msg: "hello vue3", texts: "你好 vue3" }; }, methods: { getName() { this.name = "李四"; this.age=24; } } }; </script>
<style> </style>
|
现象:点击按钮
,页面上原本的数据张三
20
改变成了 李四
24
那如果需求为:name和age单独维护,而不是包装成一个对象呢;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <template> <!-- 在vue2 中 template里面只能有唯一的一个根标签,一般设置div,在div里面写代码 --> <!-- 在vue3中,可以有多个根标签,当然也可以套个div作为唯一根标签 --> <h2>{{name}}</h2> <h2>{{age}}</h2> <hr> <button @click="getName">按钮</button> </template>
<script> import { ref } from "vue"; export default { setup() { const name = ref("张三"); const age = ref(20); console.log(name);
return { name, age }; }, data() { return { msg: "hello vue3", texts: "你好 vue3" }; }, methods: { getName() { this.name = "李四"; this.age = 24; } } }; </script>
<style> </style>
|
现象:点击按钮
,页面上原本的数据张三
20
改变成了 李四
24
那如果reactive通过解构赋值,是不是也可以完成 name和age的单独维护呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <template> <!-- 在vue2 中 template里面只能有唯一的一个根标签,一般设置div,在div里面写代码 --> <!-- 在vue3中,可以有多个根标签,当然也可以套个div作为唯一根标签 --> <h2>{{name}}</h2> <h2>{{age}}</h2> <hr> <button @click="getName">按钮</button> </template>
<script> import { reactive,ref } from "vue"; export default { setup() { const {name,age} = reactive({ name:"张三", age:20 }); return { name,age } }, data() { return { msg: "hello vue3", texts: "你好 vue3" }; }, methods: { getName() { this.name = "李四"; this.age = 24; console.log(this.name); } } }; </script>
<style> </style>
|
现象:点击按钮
,页面上原本的数据张三
20
并未发生数据的改变;但是输出的 this.name
为李四
;
发生如上的原因是因为:reactive创建响应式数据对象,不支持ES6的结构赋值,结构赋值会失去响应式的特性;
那有什么办法吗?
具体代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <template> <!-- 在vue2 中 template里面只能有唯一的一个根标签,一般设置div,在div里面写代码 --> <!-- 在vue3中,可以有多个根标签,当然也可以套个div作为唯一根标签 --> <h2>{{name}}</h2> <h2>{{age}}</h2> <hr> <button @click="getName">按钮</button> </template>
<script> import { reactive,ref,toRefs } from "vue"; export default { setup() { const {name,age} = toRefs(reactive({ name:"张三", age:20 })); return { name,age } }, data() { return { msg: "hello vue3", texts: "你好 vue3" }; }, methods: { getName() { this.name = "李四"; this.age = 24; console.log(this.name); } } }; </script>
<style> </style>
|
现象:点击按钮
,页面上原本的数据张三
20
改变成了 李四
24
computed计算属性
1 2 3 4 5
| export default{ computed:{ } }
|
使用方式1:(和2一样)
1 2 3 4 5
| export default{ computed:{ } }
|
使用方式2:(在setup中方式1不可用)
1 2
| import {computed} from "vue"; const computedData = computed(()=>{})
|
小案例:使用方式二,完成字符串的反转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <template> <!-- 在vue2 中 template里面只能有唯一的一个根标签,一般设置div,在div里面写代码 --> <!-- 在vue3中,可以有多个根标签,当然也可以套个div作为唯一根标签 --> <h2>{{msg}}</h2> <h2>{{msgReverse}}</h2> <hr> <button @click="getName">按钮</button> </template>
<script> import { reactive,ref,toRefs,computed } from "vue"; export default { setup() { const {name,age} = toRefs(reactive({ name:"张三", age:20 }));
const msg = ref("hello world"); const msgReverse = computed(()=>{ return msg.value.split('').reverse().join(''); });
return { name,age,msg,msgReverse } }, data() { return { }; }, methods: { getName() { this.name = "李四"; this.age = 24; console.log(this.name); } } }; </script>
<style> </style>
|
现象:页面上展示hello world
dlrow olleh
watch侦听器
1 2 3 4 5
| export default{ watch:{ } }
|
使用方式1:(和2一样)
1 2 3 4 5
| export default{ watch:{ } }
|
使用方式2:(在setup中方式1不行)
1
| import {watch} from "vue";
|
使用ref创建响应式数据时进行侦听(基本数据类型)
基本数据类型的监听,在vue3中被ref定义的数据通常在使用时需要在后面加一个.value,但是这里需要监听整个RefImpl对象才能起作用,所以说在写watch时不需要加.value
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <template> <!-- 在vue2 中 template里面只能有唯一的一个根标签,一般设置div,在div里面写代码 --> <!-- 在vue3中,可以有多个根标签,当然也可以套个div作为唯一根标签 --> <input type="text" v-model="names"> <hr> <input type="text" v-model="names"> </template>
<script> import { reactive,ref,toRefs,computed,watch } from "vue"; export default { // 在组件渲染完成之前自动执行,所以不能在setup中通过this访问组件对象 // 组件相同的代码块放到setup里面,让组件在第一次创建的时候执行里面的逻辑,完成对应的操作; setup() {
// 使用ref对基本数据类型进行响应式 const names = ref(""); //定义一个names响应式数据,值为空 // 使用watch 侦听数据的变化; 一共两个参数,第一个参数为监听的对象,第二个参数是个回调函数,函数内置两个参数: // value: 系统自动注入,更新之后的最新数据 // oldValue: 系统自动注入,更新之前的数据 watch(names,(value,oldValue)=>{ console.log(value,oldValue); }); //注意: // 基本数据类型的监听,在vue3中被ref定义的数据通常在使用时需要在后面加一个.value,但是这里需要监听整个RefImpl对象才能起作 // 用,所以说在写watch时不需要加.value return{ names, }
}, data() { return { }; }, methods: { } }; </script>
<style> </style>
|
现象:在输入框输入 123
,打印输出 1
,12 1
,123 12
,即value与oldValue;
如果有两个数据需要监听可以直接写成两个watch函数
1 2 3 4 5 6 7
| watch(sum,(value,oldValue)=>{ console.log("SUM",value,oldValue) });
watch(msg,(value,oldValue)=>{ console.log('msg',value,oldValue); });
|
或者把监听的对象写成一个数组,这样写的话,oldValue和newValue全都会变成数组
1 2 3
| watch([sum,msg],(value,oldValue)=>{ console.log(value,oldValue) });
|
使用reactive创建响应式数据时进行侦听(引用数据类型)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| <template> <!-- 在vue2 中 template里面只能有唯一的一个根标签,一般设置div,在div里面写代码 --> <!-- 在vue3中,可以有多个根标签,当然也可以套个div作为唯一根标签 --> <input type="text" v-model="sum.name">
<hr> <h2>{{sum.name}}</h2> </template>
<script> import { reactive, ref, toRefs, computed, watch } from "vue"; export default { setup() { let sum = reactive({ name: "", age: 20, friend: { name: "李四", age: 25 } });
watch(sum, (value, oldValue) => { console.log(value, oldValue); });
return { sum }; }, data() { return {}; }, methods: {} }; </script>
<style> </style>
|
现象:value与oldValue值一样,都是value的值,其实是获取不到oldValue;
那有什么办法获取到oldValue吗?
如果只对对象中的某个属性进行监听,需要把第一个参数写成函数的形式才会生效
1 2 3
| watch(()=>sum.name, (newValue, oldValue) => { console.log(newValue, oldValue); });
|
如果要监听对象中的多个属性可以把第一个参数写成数组的形式
1 2 3
| watch([()=>sum.name,()=>sum.age], (value, oldValue) => { console.log(value, oldValue); });
|
- Composition(组合)API中调用生命周期函数
- 如果要在
setup
方法中调用组件生命周期钩子函数,在原来的生命周期钩子函数名称前加on
关键字,并且保持小驼峰的命名方式
- 因为
setup
是围绕 beforeCreate
和 created
生命周期钩子运行的,所以不需要显式地定义它们
那如何在setup中使用钩子函数呢?
1 2 3 4 5 6 7 8 9 10 11 12 13
| import {onMounted, onUpdated} from "vue";
export default { setup(){ onMounted(function(){ console.log("is mounted") }), onUpdated(()=>{ console.log("is updated") }) } }
|
h渲染函数
h函数就是vue中的createElement方法,这个函数作用就是创建虚拟dom,追踪dom变化的;
还有个作用就是渲染组件的;
系统会自动在render中注入渲染函数
1 2 3 4 5 6 7 8
| import APP from "./APP.vue";
new Vue({ render(){ return h(APP); } }).$mount("#app");
|
需要 手动 按需导入 h渲染函数
1 2 3 4 5 6 7 8 9
| import { createApp,h} from 'vue' import App from './App.vue'
createApp({ render(){ return h(App); } }).mount("#app")
|
h创建元素对象(vue2也可以的)
h(“元素名称”,{属性集合},[标签文本内容])
1 2 3 4 5 6 7 8 9
| import { createApp,h} from 'vue'
createApp({ render(){ return h("h2",{style:{color:"red"}},"hello Vue3") } }).mount("#app")
|
现象:界面显示 红色的
经h2
标签渲染的 hello Vue3
;
Teleport:瞬移组件的位置
- Teleport也是一种组件,他能够将我们的模板移动到DOM中Vue app 之外的其他位置;
- 在处理较大的Vue项目时,有逻辑处理组织代码库是很重要的。 但是,当处理某些类型的组件(如模式,通知或提示)时,模板HTML的逻辑可能位于与我们希望渲染元素的位置不同的文件中;
- 实际上,在很多时候,与我们的Vue应用程序的DOM完全分开处理时,这些元素的管理要容易得多。 所有这些都是因为处理嵌套组件的位置,
z-index
和样式可能由于处理其所有父对象的范围而变得棘手;
- 例如下面的代码,能够将
<child-component name="John" />
组件传送到#endofbody
的标签里去渲染,同时,props
参数name="John"
可以正常传递。
1 2 3
| <teleport to="#endofbody"> <child-component name="John" /> </teleport>
|
Teleport
组件,在代码中能保持原有布局层级、参数传递逻辑,但对应生成的Dom
,则传送到了to参数
指定的标签;
Teleport
主要用于“全屏模式的组件”,比如以下几种:
图片查看全屏显示
弹框(广告跳转框、提示框)
对话框(带有表单输入的、带有按钮的)
小案例:
假设我们有一些子组件,我们想在其中触发弹出的通知。 正如刚才所讨论的,如果将通知以完全独立的DOM树渲染,而不是Vue的根#app
元素,则更为简单;
首先,我们要打开index.html,即Vue中唯一的html文件,在#app同级下,创建一个div;
1 2 3 4 5
| <body> <div id="app"></div> <div id="myTelePort"></div> <script type="module" src="/src/main.js"></script> </body>
|
然后,将我们的通知组件放到新建的#myTelePort里面;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| <template> <div class="box"> <button class="boxBtn" @click="showNotification">toggle</button>
<Teleport to="#myTelePort"> <div class="showMessage" v-if="isOpen">Teleport 消息~~</div> </Teleport> </div> </template>
<script> import { ref } from "vue"; export default { setup() { const isOpen = ref(false);
var hideNotification;
const showNotification = () => { isOpen.value = true; clearTimeout(hideNotification); hideNotification = setTimeout(() => { isOpen.value = false; }, 2000); };
return { isOpen, showNotification }; } }; </script>
<style scoped> .box { width: 500px; height: 400px; margin: 0 auto; background-color: #e1e97bc7; position: relative; } .boxBtn { padding: 10px; background-color: azure; cursor: pointer; position: absolute; top: 10%; left: 10%; } .showMessage { width: 240px; height: 40px; background-color: aquamarine; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } </style>
|
现象:
在此代码段中,当按下按钮时,将渲染2秒钟的通知。 但是,我们的主要目标是使用Teleport获取通知以在我们的Vue应用程序外部渲染。
由于我们在#myTelePort
中传递了代码,因此 Vue会找到包含在index.html
中的#myTelePort
div,它会把 Teleport 内的所有代码渲染到该div
中。
效果图:
全局组件注册方式
1 2 3
| Vue.component("组件名称",{ })
|
1 2 3 4 5 6 7
| import {createApp} from "vue";
const app = createApp();
app.component("组件名称",{ })
|
具体代码如下:
入口文件 main.js中
1 2 3 4 5 6 7 8 9 10 11 12
| import { createApp} from 'vue' import App from './App.vue'
import Home from "./components/home.vue";
const app = createApp(App);
app.component("myHome",Home);
app.mount("#app");
|
根组件 App.vue中
1 2 3 4 5 6 7 8 9 10 11
| <script setup> </script>
<template>
<myHome/> </template>
<style>
</style>
|
现象:不需要在App.vue
中 引入
,注册
,调用
三部曲了;直接调用在入口文件
中全局注册的组件的名字即可;
函数式组件
- 通过函数方式定义的组件
- 特点:函数组件默认没有状态数据和生命周期
- 语法:
1 2 3 4 5 6
| import {h} from "vue";
const funComponent = function(){ return h("h1",{},'函数组件') }
|
创建一个js文件,这里为 functionComponent.js
1 2 3 4 5
| import {h} from "vue"; export default function FunctionCompon(props){ console.log(props) return h("h1",{style:{color:"red"}},props.msg) }
|
App.vue 根组件里
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script setup>
import funCompon from "./components/functionComponent.js"; </script>
<template>
<funCompon msg="函数式组件传递数据"/> <Home/> </template>
<style> </style>
|
现象:页面展示经h1
渲染的红色
的函数式组件传递数据
字样;
异步组件
普通写法
1
| const Home =()=>import("./components/home.vue");
|
高阶写法
1 2 3 4 5 6 7
| const asyncModal = { component: () => import('./Modal.vue'), delay: 200, timeout: 3000, error: ErrorComponent, loading: LoadingComponent }
|
普通写法
1 2
| import {defineAsyncComponent} from "vue"; const Home = defineSyncComponent(()=>import("./components/home.vue"));
|
高阶写法
1 2 3 4 5 6 7 8 9 10 11
| import { defineAsyncComponent } from 'vue' import ErrorComponent from './components/ErrorComponent.vue' import LoadingComponent from './components/LoadingComponent.vue'
const asyncModalWithOptions = defineAsyncComponent({ loader: () => import('./Modal.vue'), delay: 200, timeout: 3000, errorComponent: ErrorComponent, loadingComponent: LoadingComponent })
|
在App.vue 根组件里面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <script setup>
import {defineAsyncComponent} from "vue";
const Home = defineAsyncComponent({ loader:()=>import("./components/home.vue") })
</script>
<template>
<Home/> </template>
<style>
</style>
|
v-for中使用ref属性
通过ref来获取dom元素
当ref和v-for一起使用的时候,得到的ref为一个数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <template> <div id='app'> <div> <p v-for="item in 5" :key="item.name" ref="nodes"></p> </div> </div> </template>
<script> export default { mounted() { console.log(this.$refs.nodes); } } </script> <style> </style>
|
缺点: Vue 2 中,在 v-for 语句中使用ref属性时,会生成refs数组插入$refs属性中。当存在嵌套的 v-for 时,这种行为会变得不明确且效率低下。
在 v-for 语句中使用ref属性 将不再会自动在$refs中创建数组。而是,将 ref 绑定到一个 function 中,在 function 中可以灵活处理ref。
1 2 3
| <ul> <li :ref="list" v-for="(item,index) in arr" :key="index"></li> </ul>
|
选择式API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <template> <!-- vue3 ref前面有一个 : --> <p v-for="(item,index) in 5" :key="index" :ref="getRefList"></p> </template>
<script> export default { data() { return { refList: [] } }, methods: { getRefList(el) { this.refList.push(el); console.log(this.refList); } }, beforeUpdate() { this.refList = []; } }; </script>
<style> </style>
|
组合式API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <template> <!-- 这里的ref前面有一个 : --> <p v-for="(item,index) in 5" :key="index" :ref="getRefList"></p> </template>
<script> import {ref,onBeforeUpdate} from "vue"; export default { setup(){ const refList = ref([]);
const getRefList = (el)=>{ refList.value.push(el); console.log(refList) };
onBeforeUpdate(()=>{ refList.value=[]; })
return{ refList,getRefList, } } }; </script>
<style> </style>
|
v-for与v-if优先级
v-for与v-if指令同时应用于同一个元素之上,v-for指令优先级比v-if高;即 v-for指令先执行,v-if指令后执行;
vue2 简单小案例:99乘法表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <template> <div> <div v-for="(rowItem,rowIndex) in row" :key="rowIndex"> <span v-for="(colItem,colIndex) in col" :key="colIndex" v-if="colItem<=rowItem" >{{`${colItem}*${rowItem}=${colItem*rowItem} `}}</span> </div> </div> </template>
<script> export default { data() { return { row: 9, col: 9 }; }, methods: {} }; </script>
<style> </style>
|
vue3中的优先级与vue2相反,即v-if的优先级比v-for高;
自定义指令
- el:指令所绑定的元素,可以直接操作DOM。
- binding:是一个对象,包含该指令的所有信息。
- 有自定义指令中的生命周期钩子函数
什么时候需要使用?(用的其实比较少)(有局部注册和全局注册,因为用的少,所以只说全局注册(main.js)的情况)
需要对少量的普通 DOM 元素进行底层操作,这时候就会用到自定义指令
但对于需要操作大量DOM元素或者大变动时候,推荐使用组件,而不是指令
1 2
| Vue.directive("指令名称",function(el,binding,vnode){ })
|
实现高亮效果
1
| <p v-highlight="'yellow'">以亮黄色高亮显示此文本</p>
|
1 2 3 4 5
| Vue.directive('highlight', { bind(el, binding, vnode) { el.style.background = binding.value } })
|
1 2 3 4
| import {createApp} from "vue"; const app =createApp(); app.directive("指令名称",function(el,binding,vnode){ })
|
实现高亮效果
1 2
| <!-- 自定义指令 --> <p v-highLight="'yellow'">自定义指令 高亮效果</p>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { createApp} from 'vue' import App from './App.vue'
const app = createApp(App);
app.directive("highLight",{ beforeMount(el,binding,vnode){ el.style.background= binding.value; } }) app.mount("#app");
|
app.config.globalProperties全局属性
vue2中给vue注册全局功能使用的是Vue.prototype
,这样每一个vue组件都可以访问,因为每一个组件都是Vue的实例对象;
1 2 3 4
| Vue.prototype.$version = '1.0.0'
console.log(this.$version)
|
Vue3.x已经不支持直接Vue.prototype.$xxx =xxx这种方式来挂载全局对象,这是由于globalVue不再是构造函数,因此不再支持该构造函数
1 2 3 4 5 6 7
| import { createApp } from 'vue' import App from './App.vue' const app = createApp(App);
app.config.globalProperties.$vueName = 'Vue3全局挂载名称' app.mount('#app')
|
组合式API
获取方法:通过 getCurrentInstance
获取proxy
,再获取全局挂载的实例;
因为setup没有this,若按照选择式API
,可直接this.$globalNames
,这里的this
是一个Proxy
对象
1 2 3 4 5 6 7 8 9 10
| <script> import { defineComponent, getCurrentInstance } from 'vue' export default defineComponent({ setup(){ const { proxy } = getCurrentInstance() console.log(proxy.$vueName) return {} } }) </script>
|
移除属性
$children
获取子组件的实例,在vue2中,除了$refs
方法,还有$children
;
children.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <template> <h2>{{title}}</h2> </template>
<script> export default { data(){ return{ title:"this is children" } }, methods:{ changeTitle(){ this.title="vue2中的$children获取子组件的实例" } }
} </script> <style> </style>
|
parent.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <template> <div> <h2>this is parent</h2> <button @click="getChild">按钮</button> <vChild/> </div> </template>
<script> import vChild from "./child.vue";
export default { components: { vChild },
methods:{ getChild(){ this.$children[0].changeTitle(); } }, created(){ console.log(this.$children); } }; </script>
<style> </style>
|
在 3.x 中,$children
property 已被移除,且不再支持。如果你需要访问子组件实例,我们建议使用 $refs。
filter过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <template> <h2>{{context|doubleFilter}}</h2> </template>
<script> export default { data(){ return{ context:3.1415926 } }, filters:{ doubleFilter(val){ return val.toFixed(2); } } } </script>
<style> </style>
|
虽然这看起来很方便,但它需要一个自定义语法,打破了大括号内的表达式“只是 JavaScript”的假设,这不仅有学习成本,而且有实现成本。
我们建议用计算属性或方法代替过滤器,而不是使用过滤器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <template> <h2>{{doubleContext()}}</h2> <h2>{{computedContext}}</h2> </template>
<script> import { ref,computed } from "vue"; export default { setup() { const context = ref(3.1415926);
const doubleContext =()=>{ return context.value.toFixed(2); }
const computedContext= computed(()=>{ return context.value.toFixed(2); }) return { context,doubleContext,computedContext }; } }; </script>
<style> </style>
|
以上两种方法都可以实现过滤器效果,但是批量使用的话,好像得批量注册(个人观点)
vm.$set(Vue.set)
vm.$on
vue2中,通过Eventbus
,$emit
,$on
来实现兄弟组件间传值,在vue3中,$on
被移除,如果需要继续使用此功能改为使用第三方mitt
库(见下方参考文献)
参考文献
Vue3 - 环境安装和启动配置
Vue3-使用多个根标签报错
Vue3中的watch
Vue3的ref和reactive对比(总结)
ref、reactive、toRef、toRefs的区别
ES6拓展运算符
Vue中的h函数
render: h => h(App)解析
Vue的渲染函数render&h
Vue3 Teleport 简介,请过目,这个是真的好用!
Vue3 Teleport的应用示例
Vue3新特性之Teleport介绍
Vue3 学习笔记 (三)——Vue3 自定义指令
Vue2$attrs组件传值
Vue3 全局挂载对象和方法
Vue3兄弟组件传值方式mitt.js