1. vue3介绍
Vue.js 3.0
版在2020年9月18日发布Vue3
支持vue2
的大多数特性Vue3
在2022年2月7日成为新的默认版本
性能提升
- 项目打包体积更小
- 初次渲染更快, 更新渲染更快
- 需要的运行内存更小
- 使用
Proxy
代替Object.defineProperty
实现数据响应式 - 重写虚拟
DOM
的实现和Tree-Shaking
- 更好的支持
Typescript
2. 获取vue3.0
1. cdn方式
打开vue3官网->起步->安装->CDN->单击CDN的src->右键另存为(将vue.global.js
存到自己创建的文件夹)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 导入vue3源码包 -->
<script src="./vue.global.js"></script>
</head>
<body>
<!-- 应用程序的挂载点 -->
<div id="app">
<h1>{{msg}}</h1>
</div>
</body>
</html>
<script>
/*
vue2写法:
new Vue({
// 挂载应用实例
el:"#app",
// data可以写成一个对象
data:{
},
methods: {
},})
*/
// 1.创建vue实例对象
const app = Vue.createApp({
// data属性必须采用函数的写法
data() {
return {
msg: 'hello vue3'
}
},
})
console.log(app);
// 2.挂载应用实例
app.mount('#app');
</script>
2. vue cli脚手架方式
-
安装最新版本的
vue cli
脚手架npm i @vue/cli -g
-
创建项目
vue create 项目名称
-
选择
vue3
版本的默认选项 -
启动项目
npm run serve
3. vite构建工具
vite
是vue
官方发布的一款构建工具
vite
底层不是基于webpack
vite
底层是基于浏览器对ES模块语法的支持来进行项目构建, 所有构建速度很快
-
创建项目
npm init vite 项目名称
-
安装项目依赖
项目根目录运行初始化命令
npm install // 或者 npm i
-
启动项目
项目根目录运行启动命令
npm run dev
-
默认监听端口号3000
http://localhost:3000
-
(模块化理解):
tools.js
export default{ add(x,y){ return x+y; }, reduce(x,y){ return x-y; } }
index.html
方法一:
<!-- type="module"让浏览器支持ES6中的模块化语法 --> <script type="module" src="./tools.js"></script>
方法二:
<script type="module"> // 导入tools.js import tools from './tools.js'; console.log(tools); console.log(tools.add(100,200));//300 </script>
3. 新增特性
Fragment模板碎片
-
vue2
中组件的模板必须有一个唯一的根标签 -
vue3
中组件模板可以有多个根标签Home.vue
<template> <h1>{{msg}}</h1> <h1>{{test}}</h1> </template> <script> export default { data() { return { msg:"hello vue3", test:"你好,vue3", } }, } </script>
App.vue
<script setup> // 一旦在script标签上,添加了script属性,则不需要手动注册子组件 import Home from './components/Home.vue' </script> <template> <Home /> </template>
页面结果:
Composition (组合) API
- 作用: 将不同组件的相同部分抽离出来, 单独维护, 目的是提高代码复用率
1. setup
-
用法: 在组件对象中添加
setup
方法 -
该方法在组件创建完成之前自动调用, 所用不能在其内部使用
this
访问组件对象 -
该方法中返回的数据会自动和组件对象中的
data
合并 -
该方法中返回的方法会自动和组件对象中的
methods
合并export default { setup(){ } }
eg:
<template> <h1>{{name}}</h1> <h1>{{age}}</h1> <hr> <button @click="getName">getName</button> <button @click="updateName">updateName</button> </template> <script> export default { // 在组件渲染完成之前执行,所以不能在setup中通过this访问 setup(props) { // console.log(this,'setup');undefined // 在setup方法中返回的数据会自动和组件data中的数据进行合并 // 在setup中返回的方法,自动和methods对象进行合并 return { name:"张三", age:18, getName(){ console.log(this.name); }, updateName(){ this.name='李四'; // 因为不是响应式的数据,所以视图没变,实际上数据其实已经发生改变啦 console.log(this.name); }, } }, data() { return { msg:"hello vue3", test:"你好,vue3", } }, } </script>
页面结果:
2. reactive
-
作用: 帮助我们创建响应式数据对象
-
语法
import {reactive} from 'vue'; export default { setup(){ // 创建响应式数据对象 const obj=reactive({ }); return obj; } }
eg:
<template> <h1>{{name}}</h1> <h1>{{age}}</h1> <button @click="updateName">updateName</button> <button @click="age=30">updateAge</button> </template> <script> import { reactive } from "vue"; export default { setup() { // reactive:可以帮助我们创建响应式的数据对象,需要一个对象作为参数 const obj=reactive({ name:"张三", age:18, }) return obj; }, methods: { updateName(){ // 更新name this.name='李四'; console.log(this.name); }, }, } </script>
3. ref
-
作用: 基于基本数据类型(字符串, 布尔, 数值) 创建一个响应式的数据对象
-
语法
import {ref} from 'vue';
export default { setup(){ // 使用ref基于基本数据类型创建响应式数据对象 const name=ref('张三'); const age=ref(20); console.log(name); console.log(age); return { name,age } } }
-
视图中引用直接通过数据名称引用即可
{{name}}
eg:
<template> <h1>{{name}}</h1> <h1>{{age}}</h1> <hr> <button @click="updateName">updateName</button> <button @click="age=30">updateAge</button> </template> <script> import { reactive,ref } from "vue"; export default { // 在组件渲染完成之前执行,所以不能在setup中通过this访问 setup(props) { // 通过ref方法基于基本数据类型创建响应式对象 // 只有在setup中修改ref数据需要用value去修改 const name=ref("张三"); // name.value="李四"; const age=ref(18); console.log(name); return { name, age } }, methods: { updateName(){ // 更新name this.name='李四'; console.log(this.name); }, }, } </script>
4. toRefs
- 作用: 让
reactvie
方法创建的响应式数据对象, 支持ES6
的解构赋值操作(reactive
创建的响应式数据对象默认不支持ES6
解构赋值, 因为会使其失去响应式 ) - 语法: 参数必须是
reactive
创建的响应式数据对象import {torRefs} from 'vue';
eg:export default { setup(){ // 创建响应式数据对象 const {name,age}=toRefs(reactive({ name:'张三', age:20 })); return { name,age }; } }
<template> <h1>{{name}}</h1> <h1>{{age}}</h1> <hr> <button @click="updateName">updateName</button> <button @click="age=30">updateAge</button> </template> <script> import { reactive,ref,toRefs } from "vue"; export default { // 在组件渲染完成之前执行,所以不能在setup中通过this访问 setup(props) { // reactive方法创建响应式数据对象,不支持ES6的解构赋值,解构赋值会使其失去响应式的特性 // toRefs:可以将reactive创建的响应式数据对象,支持ES6的解构赋值,同时保持响应式的特性 const {name,age}=toRefs(reactive({ name:"张三", age:18, sex:"男" })) return { name, age } }, methods: { updateName(){ // 更新name this.name='李四'; console.log(this.name); }, }, } </script>
computed计算属性
vue2
export default { computed:{ // 计算属性方法 } }
vue3
-
使用方式1
export default { computed:{ // 计算属性方法 } }
-
使用方式2
import {computed} from 'vue';
const computedData=computed(()=>{})
eg:
<template> <h1>{{msg}}</h1> <h1>{{reverseMsg}}</h1> </template> <script> import { reactive,ref,toRefs,computed } from "vue"; export default { // 在组件渲染完成之前执行,所以不能在setup中通过this访问 setup(props) { const msg=ref("hello vue3"); // 实现msg的翻转 // computed:创建计算属性方法 const reverseMsg=computed(()=>{ return msg.value.split('').reverse().join(''); }) console.log(reverseMsg); return {msg,reverseMsg} }, } </script>
-
watch侦听器
vue2
export default { // 侦听器 watch:{ } }
vue3
-
使用方式1
export default { // 侦听器 watch:{ } }
-
使用方式2
- 通过
watch
方法创建的普通侦听器无法侦听到数组元素的变化, 必须使用深度侦听器 - 以下补充:
- 监听对象的某个属性
setup
中监听对象的某个属性,监听不到setup
中监听对象,监听不到某个属性的新旧值变化watch
中监听对象的某个属性,可以监听新旧值变化
- 监听数组添加属性
- 能监听,但是监听不到变化
- 备用解决思路:
toRefs
解构reactive
对象可实现监听某属性新旧值- 使用
ref
,不用reactive
import {watch} from 'vue'; export default { setup(){ // 普通侦听器 watch(被侦听的数据,function(value,oldValue){ }); // 深度侦听器 watch(被侦听的数据,function(value,oldValue){ },{deep:true}); } }
eg:
<template> <h4>普通/深度监听写法</h4> <input type="text" v-model="name" placeholder="请输入" /> <hr> <h4>监听对象的某个属性</h4> <h4>(setup中监听对象的某个属性,监听不到)</h4> <h4>(setup中监听对象,监听不到某个属性的新旧值变化)</h4> <h4>(watch中监听对象的某个属性,可以监听新旧值变化)</h4> <input type="text" v-model="user.name" /> <hr /> <h4>监听数组添加属性</h4> <h4>(能监听,但是监听不到变化)</h4> <ul> <li v-for="(item, index) in list" :key="index">{{ item }}</li> </ul> <button @click="list.push('vue3')">add item</button> <hr> <h4>toRefs解构reactive对象可实现监听新旧值</h4> <input type="text" v-model="test" placeholder="请输入" /> <button @click="addValue">加属性</button> </template> <script> import { reactive, ref, toRefs, computed, watch } from "vue"; export default { setup(props) { const name = ref(""); // watch:监听数据变化 // 普通监听器 // value:系统自动注入,更新之后的最新数据 // oldValue:系统自动注入,更新之前的数据 // watch(name,function (value,oldValue) { // console.log(value,oldValue); // }) // 深度监听器 watch(name,function (value,oldValue) { console.log(value,oldValue); },{deep:true}); // 监听对象的某个属性 const user = reactive({ name: "", }); // setup中watch不支持监听某个对象的属性 user.name --------监听不到 // watch(user.name,function (value,oldValue) { // console.log(value,oldValue);//控制台不打印 // }) // value,oldValue值一样都是value(最新值),不能监听到变化 --------监听不到变化 watch(user, function (value, oldValue) { console.log(value, oldValue); //新旧值一样 }); // const test = reactive({ // name: "qwe", // age:18 // }); const {test}=toRefs(reactive({test:'qwe',age:18})) watch(test, function (value, oldValue) { console.log(value, oldValue); }); return { name, test, user }; }, watch: {// --------可以监听变化 "user.name": function (value, oldValue) { console.log(value, oldValue); //可以正常监听 }, //vue3中对数组元素进行操作,普通监听器监听不到 // list(value, oldValue) { // console.log(value, oldValue);//不打印 // }, // 深度监听器,value,oldValue值一样都是value(最新值),不能监听到变化 list:{ handler(value,oldValue){ console.log(value,oldValue);//新旧值一样 --------监听不到变化 }, deep:true } }, data() { return { list: ["vue", "react", "angular"], }; }, methods: { addValue() { // this.test.sex = "男"; this.test=this.name; }, }, }; </script>
- 通过
-
组合API中调用生命周期钩子函数
-
如果要在
setup
方法中调用组件生命周期钩子函数: 在原来的生命周期钩子函数名称前加on
关键, 并且需要保持小驼峰的命名方式 -
不能在setup方法中调用
beforeCreate
,created
者2个生命周期钩子函数 ( 因为beforeCreate
,created
这两个生命周期钩子函数的执行时间和setup
方法接近 ) -
注意:
- 钩子函数:
beforeDestory
->beforeUnmount
- 钩子函数:
destoryed
->unmounted
- 销毁组件:
this.$destory()
->app.unmount()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R6X6Qq2p-1659530104528)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a16c02f295714778abcbd52057a3eeee~tplv-k3u1fbpfcp-watermark.image?)]
- 钩子函数:
-
语法
// 导入 import {onMounted,onUpdated,onUnmounted} from 'vue'; export default { setup(){ onMounted(function(){ console.log('组件视图挂载完成'); }); onUpdated(function(){ console.log('组件更新完成'); }); onUnmounted(function(){ console.log('组件卸载完成'); }); } }
h渲染函数
-
vue2
系统会自动在render中注入渲染函数
import App from './App.vue'; new Vue({ // h是系统自动注入的渲染函数 render(h){ return h(App); } });
-
vue3
vue3中提供了一个h渲染函数, 我们需要手动导入才能使用, 系统不会自动将其注入到render中
import { createApp,h} from 'vue' import App from './App.vue' // createApp(App).mount('#app') createApp({ render(){ // 调用渲染函数h进行组件渲染 // return h(App); // h创建元素对象: h('元素名称',{/*属性集合*/},['子节点']) return h('h1',{style:{color:'red'}},'hello vue3'); } }).mount('#app')
teleport: 瞬移组件的位置
-
teleport是一个vue3中提供的系统组件
-
作用: 可以让开发者动态指定组件内的某个视图结构的挂载位置
-
语法
<teleport to="挂载节点"> <div></div> </teleport>
函数式组件
-
通过函数方式定义的组件
-
特点: 函数组件默认没有状态数据和生命周期
-
语法
-
定义
import { h } from 'vue'; export default function FunctionComponent(props){ // console.log(props); return h('h1',{style:{color:'red'}},props.msg); }
-
调用
<script> // 导入函数组件 import FunctionComponent from './pages/FunctionComponent'; export default { // 注册组件 components: { FunctionComponent } }; </script> <template> <!--调用函数组件--> <FunctionComponent msg="hello function component"/> </template> <style> </style>
-
全局属性app.config.globalProperties
-
作用: 给Vue注册全局功能( 给每一个vue组件提供全局的属性或者方法 )
-
vue2中创建vue应用
const app=new Vue({ });
- vue2中给Vue注册全局功能: 在Vue的原型对象添加属性或者方法, 这样每一个vue组件都可以访问, 每一个vue组件都是Vue的实例对象( 利用面向对象的继承性 )
-
vue3中创建应用对象
const app=Vue.createApp({ });
- vue3中给vue注册全局功能: 必须借助全局属性
app.config.globalProperties
- vue3中给vue注册全局功能: 必须借助全局属性
-
需求: 希望在每一个vue组件中都可以通过组件对象访问axios
-
下载安装axios
npm i axios
-
必须使用app.config.globalProperties全局注册axios
app.config.globalPropperties.$axios=axios;
-
插件注册
-
vue2中插件注册
Vue.use();
-
vue3中插件注册
// app: 是通过Vue.createApp()方法创建的应用对象 app.use();
-
功能: 将axios自动注册成全局对象( 在每一个组件中都可以通过this来访问axios )
-
开发vue插件
-
函数方式
// app: 系统自动注入, 应用对象 // options: 调用者通过app.use()方法手动传入的配置对象(可选) function MyAxios(app,options){ }
-
对象方式
const MyAxios={ // app: 系统自动注入的应用对象 // options: 调用者通过app.use()方法手动传递的配置对象(可选) install(app,options){ } }
// 导入axios import axios from 'axios'; export default { // app: 系统自动注入的应用对象 install(app){ // 通过app.config.globalProperties全局注册axios app.config.globalProperties.$http=axios; } }
-
-
4. 更新特性
生命周期钩子函数
-
销毁阶段生命周期钩子函数名称发生了变换
-
vue2中
export default { // 销毁前 beforeDestroy(){ }, // 销毁后 destroyed(){ } }
-
vue3中
- vue3中从组件对象中移除了销毁组件实例的方法
$destroy
export default { // 销毁前 beforeUnmount(){ }, // 销毁后 unmounted(){ } }
- vue3中从组件对象中移除了销毁组件实例的方法
全局组件注册方式
-
vue2中注册全局组件
Vue.component('组件名称',{ /* 组件对象 */ });
-
vue3中注册全局组件
import {createApp} from 'Vue'; // 创建实例对象 const app=createApp(); // 注册全局组件 app.component('组件名称',{ // 组件对象 })
异步组件
- vue2中
const Home=()=>import('./pages/Home.vue');
const asyncModal = {
// vue2中通过component属性节点指定目标组件
component: () => import('./Modal.vue'),
// 延时时长
delay: 200,
// 超时时间
timeout: 3000,
// 错误处理组件
error: ErrorComponent,
// 加载中的组件
loading: LoadingComponent
}
- vue3中
import {defineAsyncComponent} from 'vue';
const Home=defineAsyncComponent(()=>import('./pages/Home.vue'));
const asyncModalWithOptions = defineAsyncComponent({
// vue3中通过loader属性节点指定目标组件
loader: () => import('./Modal.vue'),
// 延时时长
delay: 200,
// 超时时间
timeout: 3000,
// 错误处理组件
error: ErrorComponent,
// 加载中的组件
loading: LoadingComponent
})
v-for指令中使用ref属性
-
vue2中
<ul> <li ref="list" v-for="item in list" :key="item"></li> </ul>
this.$refs.list[0]; this.$refs.list[1];
-
vue3中: 需要手动维护一个数组用来保存每一个子元素对象
<ul> <li :ref="addRef" v-for="item in list" :key="item"></li> </ul>
export default { data(){ return { // 保存列表元素的元素对象 refList:[] } }, methods:{ // el: 系统自动注入的参数, 子元素的元素对象 addRef(el){ this.refList.push(el); } }, // 在组件更新之前, 清空数组中的元素, 否则该数组中的元素会越来越多 beforeUpdate(){ this.refList=[]; } }
v-if和v-for结合使用
-
vue2中
v-if和v-for指令同时应用于同一个元素之上, v-for指令优先级会更高, v-if会作用域v-for循环遍历产生的每一个子元素之上
-
vue3中
v-if指令的优先级高于v-for指令, v-if指令执行, 然后执行v-for指令
自定义指令定义方式
-
vue2中
Vue.directive('指令名称',function(el, binding, vnode){ });
-
vue3中
然而,在 Vue 3 中,我们为自定义指令创建了一个更具凝聚力的 API。正如你所看到的,它们与我们的组件生命周期方法有很大的不同,即使钩子的目标事件十分相似。我们现在把它们统一起来了:
- created - 新增!在元素的 attribute 或事件监听器被应用之前调用。
- bind → beforeMount
- inserted → mounted
- beforeUpdate:新增!在元素本身被更新之前调用,与组件的生命周期钩子十分相似。
- update → 移除!该钩子与
updated
有太多相似之处,因此它是多余的。请改用updated
。 - componentUpdated → updated
- beforeUnmount:新增!与组件的生命周期钩子类似,它将在元素被卸载之前调用。
- unbind -> unmounted
const app=createApp(); app.directive('指令名称',function(el, binding, vnode){ });
$attrs
包含class&style
-
vue2中
this.$attrs
包含调用组件的时候通过自定义属性所传递的数据, 但是不包含class属性和style属性
-
vue3中
-
this.$attrs
: 调用组件的时候通过自定义属性所传递的所有数据, 包含class属性和style属性- 当子组件只有一个唯一的根元素的时候, 系统会将所有的自定义属性平移到组件视图的根元素上
- 如果组件视图存在多个根元素, 则不会处理
-
emits属性节点
-
vue3中针对组件对象新增的属性节点
-
作用:
- 告诉系统组件调用标签上添加的事件是一个自定义事件, 需要通过自定义事件的方式进行触发( vue3中默认会将组件调用标签上的事件作为原生事件进行触发)
- 避免当我们的定义的自定义事件名称和系统事件名称出现冲突的时候, vue3直接把自定义事件当做系统事件来触发
<template> <div> <h1>EmitsTest子组件</h1> <button @click="$emit('click')">触发自定义事件click</button> </div> </template> <script> export default { // 通过emits属性节点告诉vue, 这是一个自定义事件, 需要通过自定义事件的方式进行触发 emits:['click'] } </script> <style> </style>
过渡动画
-
过渡动画选择器名称的变化
- v-enter=>v-enter-from: 元素进入之前的过渡动画选择器名称
- v-leave=>v-leave-from: 元素离开之前的过渡动画选择器名称
-
transition
组件属性的变化- enter-class=>enter-from-class: 指定元素进入之前需要的过渡动画选择器
- leave-class=>leave-from-class: 指定元素离开之前需要的过渡动画选择器
-
transition-group组件默认不再自动渲染一个span标签, 但是依然可以通过tag属性指定transition-group渲染的元素名称
v-model实现组件双向数据绑
-
vue2中使用v-model实现组件双向数据绑定
<Modal v-model="isShow"></Modal>
-
在组件的调用标签上添加了一个自定义属性value, 通过value属性向组件内部传递数据
-
在组件的调用标签上通过v-on监听了input事件
<Modal :value="isShow" v-on:input="isShow=$event"></Modal>
-
希望对v-model指令背后使用的自定义属性名称和自定义事件名称进行修改
- 在组件内部通过model属性节点对自定义属性名称和自定义事件名称进行修改
export default{ model:{ prop:'', // 修改自定义属性名称 event:'' // 修改自定义事件名称 } }
-
-
vue3中使用v-model指令实现组件双向数据绑定
<Modal v-model="isShow"></Modal>
-
在组件的调用标签上添加了一个自定义属性
modelValue
, 通过modelValue
向组件内部传递数据 -
在组件的调用标签上通过
v-on
监听了update:modelValue
-
希望对v-model指令背后的自定义属性名称和自定义事件名称进行修改
<Modal v-model:isShow="isShow"></Modal>
- 在组件内部要通过
isShow
来接收数据 - 在组件内部要通过
$emit()
触发事件update:isShow
- 在组件内部要通过
-
vue3中允许在同一个组件的调用标签上多次使用v-model指令
<Modal v-model:isShow="isShow" v-model:msg="msg"/>
- 相当于在组件的调用标签上添加了自定义属性
isShow
,msg
, 同时监听了两个自定义事件update:isShow
和update:msg
- 相当于在组件的调用标签上添加了自定义属性
-
5. 移除特性
$children属性
- vue2中可以通过组件属性
$children
获取子组件的组件对象 - vue3中建议使用
ref
属性来操作子组件
native事件修饰符
-
vue2中可以通过native事件修饰符修饰某个事件, 被修饰的事件将被作为原生事件处理, 可以在子组件的内部使用原生方式对其进行触发
-
vue3中移除了native事件修饰符
-
vue3中如果希望某个事件可以在子组件的内部被当做原生事件来处理?
-
在vue3中组件调用标签上注册的事件, 默认都会当做原生的事件进行触发
-
在vue3中如果我们希望组件调用标签上所注册的事件是一个自定义事件, 可以在组件内部通过属性节点
emits
来进行声明, 在emits
属性节点中生命过的事件就不会当做系统事件来处理export default { // 此处声明过的事件, 会认为是自定义事件, 必须通过自定义事件的方式进行触发, 即使事件明层和系统事件名称冲突, 也不会通过系统事件的触发方式触发该事件 emits:['click'] }
-
filter过滤器
-
vue2中过滤器的使用
-
作用: 对视图中即将输出的数据进行预处理
-
定义
- 全局过滤器: 在全局作用域内都可以使用
// data: 系统自动注入, 待处理的数据 Vue.filter('过滤器名称',function(data){ // 必须返回处理结果 });
- 私有过滤器: 只能在定义地方使用
export default { filters:{ // data: 系统自动注入, 待处理的数据 过滤器名称:function(data){ // 必须返回处理结果 } } }
-
使用: 只能在插值表达式和v-bind指令中使用
{{待处理数据 | 过滤器名称}}
-
-
vue3中建议使用自定义方法或者computed计算属性来代替过滤器
vm.$set(Vue.set)
-
vue2中可以使用
vm.$set
方法解决通过数组下标方式向数组中添加元素, 视图不更新的问题vm.$set(目标数组,数组下标,数组元素);
vm.$set(目标对象,属性名,属性值);
-
vue3中移除实例方法
vm.$set
和静态方法Vue.set
vm.$destroy
- vue2中, 可以通过实例方法
$destroy
销毁vue组件 - vue3中移除实例方法
$destroy
,
vm.$on
- vue2中, 可以通过$on方法监听自定义事件
- vue3中, 移除了 o n 方法 , 但是 on方法, 但是 on方法,但是emit方法没有移除
vm.$once
- vue2中, 可以通过$once方法监听自定义事件, 但是只能监听一次
- vue3中, $once方法被移除了
vm.$off
- vue2中可以通过$off方法注销一个自定义事件
- vue3中$off方法被移除了
6. vue-router路由模块
vue2中路由的使用流程
-
下载安装
npm i vue-router
-
导入路由模块
import Router from 'vue-router';
-
注册插件
Vue.use(Router);
-
创建路由实例对象
const router=new Router({ routes:[], // 引用路由规则数组 mode:'hash' // 指定路由模式, 可选值hash(默认路由模式),history });
-
挂载路由实例对象
new Vue({ el:'#app', router // 挂载路由实例对象 });
-
在根组件中添加路由占位符
<router-view/>
vue3中路由模块的使用流程
-
下载安装
npm i vur-router@next
-
导入路由模块
import {createRouter,createWebHashHistory,createWebHistory} from 'vue-router';
-
创建路由实例
const router=createRouter({ routes:[], // 路由规则 //必须手动指定路由模式, createWebHashHistory: 提供的是访问地址带#的路由模式 // createWebHistory: 提供的是访问地址不带#的路由模式 history:createWebHashHistory() });
-
注册路由实例
//app: 通过Vue.createApp()创建的应用实例 app.use(router);
-
在根组件中添加路由占位符
<router-view/>
路由传参
动态路由
-
定义动态路由规则
const routes=[ { path:'/goods/:id', component:Goods } ]
-
进行路由传参
<router-link to="/goods/1"></router-link>
-
获取动态路由参数
this.$route.params.id
查询字符串方式
-
在页面访问地址中拼接查询字符串
<router-link to="/news?id=1&title=娱乐新闻">新闻详情</router-link>
-
在目标页面中获取查询字符串参数
this.$route.query.id this.$route.query.title
嵌套路由
-
配置嵌套路由规则
const routes=[ { path:'/ucenter', component:Ucenter, // 嵌套路由规则 children:[ { name:'order', path:'/order', component:Order }, { name:'address', path:'/Address', component:Address } ] } ]
-
父级路由对应的视图中添加二级路由占位符
<router-view/>
7. element-plus组件库
下载安装
npm i element-plus
完整导入
-
将element-plus组件库中提供的所有组件全部导入我们自己的vue3的项目中
-
缺点: 导致项目最终构建出来的文件体积过大, 性能不好
main.js
// 导入element-plus核心文件 import Element from 'element-plus'; // 导入element-plus的样式文件 import 'element-plus/dist/index.css'; // 全局注册组件库 const app=Vue.createApp(App); app.use(Element);
按需导入(vite)
-
安装一个开发依赖
npm i unplugin-vue-components
-
在项目配置文件中添加配置项
vite.config.js
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' +import Components from 'unplugin-vue-components/vite' +import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ vue(), + Components({ + resolvers: [ElementPlusResolver()], + }), ] })
-
重启开发服务器
npm run dev
按需导入(vue-cli)
-
安装插件
npm install unplugin-vue-components
-
修改项目配置文件(
webpack
进行配置)vue.config.js
const Components = require('unplugin-vue-components/webpack') const { ElementPlusResolver } = require('unplugin-vue-components/resolvers') module.exports={ // webpack的配置节点 configureWebpack:{ plugins: [ Components({ resolvers: [ElementPlusResolver()], }), ] } }
-
重启开发服务器
npm run serve
8. vuex状态管理工具
vue2中使用vuex状态管理工具
-
下载安装
npm i vuex
-
导入
import Vuex from 'vuex'
-
注册
Vue.use(Vuex)
-
创建数据仓储对象
const store=new Vuex.Store({ state:{ // 状态数据 }, mutaionts:{ // 操作state状态数据的同步方法 }, actions:{ // 发送数据请求的异步方法 }, getters:{ // 计算属性方法 }, modules:{ // 子模块 } })
-
挂载数据仓储对象
/mian.js
new Vue({ // 注册数据仓储对象 store });
-
在组件中使用数据仓储对象
this.$store
vue3中使用vuex状态管理工具
-
下载安装
npm i vuex@next
-
导出
import {createStore} from 'vuex';
-
创建数据仓储对象
const store=createStore({ state:{ // 状态数据 }, mutations:{ // 操作state状态数据的同步方法 }, actions:{ // 发送数据请求的异步方法 }, getters:{ // 计算属性方法 }, modules:{ // 子模块 } });
-
注册仓储对象
// app:Vue.createApp()创建的应用对象 app.use(store);
9. 案例
留言板案例
<template>
<div class="container">
<h1>留言板</h1>
<div class="line"></div>
<div class="well">
<div class="form-group">
<input v-model="nickname" class="form-control" type="text" placeholder="昵称" />
</div>
<div class="form-group">
<textarea v-model="content" class="form-control" placeholder="请输入留言内容" cols="30" rows="3"></textarea>
</div>
<div class="fotm-group">
<div @click="messageAdd" class="btn success">提交留言</div>
</div>
</div>
<div class="line"></div>
<div class="well success" v-for="(item,index) in list" :key="item.id">
<i @click="messageDel(index)" class="close">x</i>
<p>昵称: {{item.nickname}}</p>
<p>留言内容: {{item.content}}</p>
</div>
</div>
</template>
<script>
import { reactive, toRefs } from "vue";
export default {
setup() {
// 创建响应式数据对象
const data = toRefs(
reactive({
nickname: "", // 昵称
content: "", // 留言内容
// 留言列表
list: [
// {
// id: 1,
// nickname: "jack",
// content: "留言内容",
// },
// {
// id: 2,
// nickname: "rose",
// content: "留言内容",
// },
],
})
);
// 删除留言内容
// index: 将要被删除的留言内容在留言列表数组中的索引
const messageDel=(index)=>{
// 根据数组索引号通过splice()方法删除数组元素
data.list.value.splice(index,1);
}
// 添加留言
const messageAdd=()=>{
if(data.nickname.value===''){
return alert('请输入昵称');
}
if(data.content.value===''){
return alert('请输入留言内容');
}
// 手动计算留言id
const id=data.list.value.length>0?data.list.value[data.list.value.length-1].id+1:1;
// 手动构造留言对象
const msg={id,nickname:data.nickname.value,content:data.content.value}
// 将留言对象追加到留言列表数组的末尾
data.list.value.push(msg);
// 重置表单元素
data.nickname.value='';
data.content.value='';
}
return {
...data,
messageDel,
messageAdd
};
},
};
</script>
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.container {
width: 800px;
margin: 20px auto;
}
.form-group {
margin: 15px 0;
}
.form-control {
padding: 10px;
width: 100%;
border: 1px solid #ddd;
border-radius: 4px;
outline: none;
resize: none;
}
.btn {
border: 0;
min-width: 70px;
max-width: 120px;
line-height: 35px;
text-align: center;
color: #555;
background: #eee;
border-radius: 4px;
cursor: pointer;
outline: 0;
margin: 0 2px;
padding: 4px;
}
.btn.danger {
background-color: #d9534f;
color: #fff;
}
.btn.success {
background-color: #337ab7;
color: #fff;
}
.line {
border-bottom: 1px solid #ddd;
margin: 10px 0;
}
.well {
position: relative;
padding: 10px;
border-radius: 5px;
background: #f0f0f0;
margin: 10px 0;
color: #444;
}
.well.success {
background: #dff0d8;
}
.well .close {
position: absolute;
top: 10px;
right: 10px;
cursor: pointer;
font-style: normal;
}
.well p {
padding: 6px;
}
</style>