介绍
下一个目标 全面拥抱Vue3时代
# Vue3的特性
目标:了解vue3现状,以及它的优点,展望它的未来
- Vue3现状:
- vue-next (opens new window) 2020年09月18日,正式发布vue3.0版本。但由于刚发布周边生态不支持,大多数开发者处于观望。
- 现在主流组件库都已经发布了支持vue3.0的版本,其他生态也在不断地完善中,这是趋势。
- element-plus (opens new window) 基于 Vue 3.0 的桌面端组件库
- vant (opens new window) vant3.0版本,有赞前端团队开源移动端组件库
- ant-design-vue (opens new window) Ant Design Vue 2.0版本,社区根据蚂蚁 ant design 开发
- Vue3优点:
- 数据具备响应式 无需调用
$set
方法 - 最火框架,它是国内最火的前端框架之一,官方文档 (opens new window) 中文文档 (opens new window)
- 性能提升,运行速度事vue2.x的1.5倍左右
- 体积更小,按需编译体积比vue2.x要更小
- 类型推断,更好的支持Ts(typescript)这个也是趋势(js的超集)
- 高级给予,暴露了更底层的API和提供更先进的内置组件
- ★选项API-----组合API (composition api) ,能够更好的组织逻辑,封装逻辑,复用逻辑
Vue3展望:
- 这是趋势,越来越多的企业将来肯定会升级到Vue3.0
- 大型项目,由于对Ts的友好越来越多大型项目可以用Vue3.0
个人展望
- 全面拥抱Vue3 有机会学习TS语言
总结: 为什么要学 vue3 ?
适应市场学习流行的技术提高提升自己竞争力,给自己加薪。
# 创建Vue应用
目标:掌握如何创建vue3应用实例
- 基于Vue脚手架创建项目
# 1. 安装脚手架
npm i @vue/cli -g
# 2. 创建vue3项目(选项中选择v3版本,其他和之前一样)不能有大写
vue create 项目名称
- 项目代码结构分析
落地代码:
- 跟组件
App.vue
<template>
<!-- 模板中不要求有唯一的跟节点 -->
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view />
</template>
<style lang="less"></style>
- 入口文件
main.js
// Vue2.0的导入方法
// import Vue from 'vue'
// Vue3.0的导入方法
// 这种导入API的方式可以减少打包后代码的体积(按需导入)
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// Vue3的写法
// createApp(App)的返回值是 Vue实例对象
// use方法用于配置插件(导入插件时候 需要这样导入)
createApp(App).use(store).use(router).mount('#app')
// Vue2的写法
// new Vue({
// router,
// store,
// render: h => h(App)
// }).$mount('#app')
总结:
- 如何创建vue应用实例?createApp
- 如何创建路由实例?createRouter
- 如何创建Store实例?createStore
注意:模板中可以没有唯一的跟节点。
# 选项API和组合API
目标:理解什么是选项API写法,什么是组合API写法。
- 选项API,之前写代码的风格,代码都是写到组件的配置选项中(methods/props/computed....)
- 组合API,代码不再完全写到选项中,而是有新的规则
- 选项API与组合API对比分析
什么是选项API写法:Options ApI
- 咱们在vue2.x项目中使用的就是
选项API
写法- 代码风格:data选项写数据,methods选项写函数...,一个功能逻辑的代码分散。
- 优点:易于学习和使用,写代码的位置已经约定好
- 缺点:代码组织性差,相似的逻辑(功能)代码不便于复用,逻辑复杂代码多了不好阅读。
什么是组合API:Composition API
- 以功能为单位组织代码结构,后续重用功能更加方便。
- Vue中导入方法 需要用 import按需加载 才可以在
setup()
里面使用该方法
总结: 知道选项API和组合API的写法区别,建议大家使用组合API在vue3.0项目中。
# 组合API setup()
设置组合api方法
目标:掌握
setup()
函数的基本使用作用: 几乎所有的js逻辑代码都是写到setup方法里
setup()是什么:
setup()
一般只能执行一次 如果数据动态修改 需要用watch
监听- 就比如 动态获取路由数据的时候 需要重新调用路由数据 获取相应的动态路由数据
- 如果监控的一级路由 存在二级路由 需要屏蔽
// 储存一级分类相关的商品数据 const goods = ref([]) // watch监听动态获取的路由数据 然后重新获取新的路由数据 watch(() => route.params.id, (newId) => { // 获取路由数据 findTopCategory(newId).then(data => { // 储存一级分类相关的商品数据 goods.value = data.result.children }) }, { // 页面加载时候 启动监控(相当于生命周期 打开页面调用) immediate: true })
- 就比如 动态获取路由数据的时候 需要重新调用路由数据 获取相应的动态路由数据
setup()
是一个新的组件选项,作为组件中使用组合API的起点(把一个功能全部组合起来)setup()
调用的时间相当于Vue2的created()
执行在组件创建之前- 无法使用this 需要在结尾
return
出去 - 这就意味着在
setup()
函数中this
还不是组件实例,this
此时是undefined
- 无法使用this 需要在结尾
在模版中需要使用的数据和函数,需要在
setup()
中return
出去。Vue中导入方法 需要用 import按需加载 才可以在
setup()
里面使用该方法setup()
在同一个组件中 不可以多次调用
演示代码
<template>
<!-- 模板中不要求有唯一的根节点 -->
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
<div>
<!-- 设置点击事件 -->
<button @click="fn">点我</button>
<!-- 差值表达式插进去 -->
{{msg}}
</div>
</div>
<router-view />
</template>
<script>
export default {
name: 'App',
setup () {
// setup函数的触发时机发生在(生命周期函数)之前 此时组件尚未创建 不可以用this
// 在该函数中是没有this的 此时Vue组件实例尚未创建成功
// setup () 该函数返回值用于给组件模板提供信息(数据和函数)
// 设置一个常量
const msg = 'hello' // 默认定义的数据不是响应式的 需要单独设置相应式
// 设置一个方法
const fn = () => {
console.log('这个是方法')
}
// setup ()储存的数据 方法 需要return出去使用 无需this
// 返回设置的数据 和 设置的方法
return {
msg,
fn
}
}
}
</script>
总结: setup
组件初始化之前执行,它返回的数据和函数可在模版使用。
注意:该函数不可以访问this,this的值为undefined
# 组合API 生命周期(钩子函数)
目标:掌握使用组合API写法的生命周期钩子函数
- Vue中导入方法 需要用 import按需加载 才可以在
setup()
里面使用该方法 - Vue3中也支持 Vue2里面的声明周期 但是不推荐
- Vue3声明周期都是函数 需要用箭头函数方法设置
- Vue3中的生命周期(钩子函数)
onBeforeMount(()=>{ })
挂载DOM前 (常用)onMounted(()=>{ })
挂载DOM后 (常用)setup(()=>{ })
创建实例前 (实际上就是Vue2 的created
)onBeforeUpdate(()=>{ })
更新组件前onUpdated(()=>{ })
更新组件后onBeforeUnmount(()=>{ })
卸载销毁前onUnmounted(()=>{ })
卸载销毁后
对比Vue2.x 左Vue2 -> 右Vue3
beforeCreate
->setup()
created
->setup()
beforeMount
->onBeforeMount
mounted
->onMounted
beforeUpdate
->onBeforeUpdate
updated
->onUpdated
beforeDestroy
->onBeforeUnmount
destroyed
->onUnmounted
errorCaptured
->onErrorCaptured
所有的声明周期方法 都可以在setup()
里面多次调用 (一样声明周期按js默认顺序排序)
Vue2生命周期示例图
演示代码
<template>
<div class="container">
container
</div>
</template>
<script>
// 必须按需导入 Vue3组合api
import { onBeforeMount, onMounted } from 'vue'
export default {
setup () {
// DOM渲染前调用onBeforeMount方法
onBeforeMount(()=>{ // 声明周期是函数方法
console.log('DOM渲染前',document.querySelector('.container'))
})
// DOM渲染后调用onMounted方法
onMounted(()=>{
console.log('DOM渲染后1',document.querySelector('.container'))
})
// setup () 里面可以多次调用一样的声明周期方法
onMounted(()=>{
console.log('DOM渲染后2',document.querySelector('.container'))
})
},
}
</script>
总结:
- 组合API的生命周期钩子有7个,
- 可以多次使用同一个钩子,
- 执行顺序和书写顺序相同。
注意:
- 兼容Vue2的生命周期,但是不建议在Vue3的项目中使用Vue2的API
- beforeCreate和created生命周期在Vue3中被setup替代了
- Vue3的生命周期直接也是在setup中进行触发,函数名称前面都添加on
# 组件拆分后 导入 生命周期
- 如果子组件设置了生命周期 那么必须在父组件引入该生命周期
- 子组件的声明生命周期 设置为常量 传给父组件让父组件的生命周期调用该变量
调用例子
- 子组件的设置
js文件
- 子组件的生命周期设置为常量 进行导出
// 导入 Vue3的ref方法
import { ref } from '@vue/reactivity'
// 导入 Vue3的computed方法
import { computed } from '@vue/runtime-core'
// 设置一个常量
const retx = ref(1)
// 设置函数方法
const changeN = () => {
retx.value += 1
}
// 设置计算属性
const newretx = computed(() => {
return retx.value + 1
})
//! 设置需要导出的生命周期
const onMounted = () => {
changeN()
}
// 设置需要导出的数据内容
export default {
onMounted,
changeN,
newretx,
retx
}
- 父组件的设置
vue文件
- 父组件接收子组件的生命周期常量 导入其自身的生命周期中
<template>
<!-- 模板中不要求有唯一的根节点 -->
<div id="nav">
<div>我的初始值{{retx}}</div>
<div>我的更新值{{newretx}}</div>
<button @click="changeN">点我</button>
</div>
</template>
<script>
// 导入子组件
import model from './views/model/demob'
// 导入Vue3的生命周期
import { onMounted } from '@vue/runtime-core'
export default {
setup () {
// 导入子组件设置的声明周期
onMounted(() => model.onMounted)
// 把子组件的所有数据 导入到父组件中用来调用(声明周期可以不直接导入)
return { ...model }
}
}
</script>
# 组合API reactive()
设置多个复杂对象响应式数据 (我用)
目标:掌握使用reactive函数定义响应式数据(多个数据的对象)
- Vue中导入方法 需要用 import按需加载 才可以在
setup()
里面使用该方法 reactive()
是一个函数,它可以定义一个复杂数据类型,成为响应式数据。- 通常用const直接设置的值 不是响应式 数据修改时候 不会动态修改 需要设置
reactive()
方法
- 通常用const直接设置的值 不是响应式 数据修改时候 不会动态修改 需要设置
<template>
<!-- 模板中不要求有唯一的根节点 -->
<div id="nav">
<button @click="change">点击</button>
<div>{{ret.name}}</div>
</div>
<router-view />
</template>
<script>
// 按需导入reactive()方法
import { reactive } from 'vue'
export default {
setup (props) {
// 把设置的常量数据 变为响应式数据 这样数据更新的时候 可以及时更新到设置的数据
const ret = reactive({
name: '你好',
age: '500'
})
// 设置更新数据的方法
const change = () => {
ret.name = '万岁'
}
// 返回设置的数据 和 设置的方法
return { ret, name }
}
}
</script>
- 可以设置多个
reactive()
但是用toRefs()
解构的数据只能存在一个
// 第一个reactive()
const data = reactive({
// 声明 scrollReveal组件
scrollReveal: scrollReveal(),
// 打字机内容
typewriter: res.my_typewriter,
})
// 第二个reactive()
const state = reactive({
// 顶部显示的状态位
header: 0
})
// 结构导出 只能结构一个reactive() 不能结构多个
const dataOut = toRefs(data)
// 结构导出 只能结构一个reactive() 不能结构多个
return { ...dataOut, state, gotoCV }
总结: 通常是用来定义响应式对象数据
# 组合API ref()
设置单个简单的响应式数据 (我不用)
目标:掌握使用ref函数定义响应式数据,一般用于简单类型数据(单个数据 单个数组)
- ref函数,常用于简单数据类型定义为响应式数据
watch()
监听ref
简单数据时候初始值不能为null 可以用reactive()
来设置null
- ref函数 可以储存 字符串 和 数组(数组中可包裹对象) (单个数据 单个数组)
- 在修改值,获取值的时候 需要.value
- 在模板中使用ref申明的响应式数据 可以省略.value
- 如果ref的值 在模板
template
中进行数据修改时候 无需添加.value
因为能在模板中修改的值 都不需要添加.value
<template>
<div class="container">
<!-- 在模板中使用ref申明的响应式数据,可以省略.value -->
<div>{{name}}</div>
<div>{{age}}</div>
<button @click="updateName">修改数据</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'App',
setup () {
// 1.name数据
const name = ref('ls')
console.log(name)
const updateName = () => {
// 在修改值获取值的时候,需要.value
name.value = 'zs'
}
// 2.age数据
const age = ref(10)
// ref常用定义简单数据类型的响应式数据
// 其实也可以定义复杂数据类型的响应式数据
// 对于数据未之的情况下 ref 是最适用的
return {name, age, updateName}
}
}
</script>
总结
- 当你明确知道需要的是一个响应式数据 对象 那么就使用 reactive 即可 其他情况使用ref
# 组合API toRefs()
解构响应数据 (我用)
toRefs()
有两个场景- 第一个是 解构
reactive()
声明的数据 可以去除命名的变量 结构方便调用场景 - 第二个是 父子传值的时候 接收父组件传来的参数 自动解构 直接调用即可
- 第一个是 解构
- Vue中导入方法 需要用 import按需加载 才可以在
setup()
里面使用该方法 - 结构后获取内部数据值的时候需要加.value 如果
return
不需要value
// 使用了解构赋值语法创建了变量myTitle
const { myTitle } = toRefs(props)
// 使用数据的时候 必须要加value
console.log(myTitle.value)
# 结构方便调用场景
目标:掌握使用
toRefs()
函数转换响应式对象中某个属性为单独响应式数据,并且值是关联的。使用场景:把
reactive()
里面单独的响应式数据 进行单独拆分时候使用
- 单独定义响应式数据(在多个响应式中 提取单独一个的值)
toRefs()
和reactive()
相互关联reactive()
定义了多个响应式 如果想单独调用 需要用到toRefs()
单独调用出来(需要return
返回)- 如果你没有结构 想把
reactive()
数据全部导出 就需要扩展运算符...
演示代码
<template>
<div>
<div class="overall-header">
<!-- 4.通过toRefs解构后可以直接调用哦 -->
<div id="reveal-top" class="overall-header-left">{{ name }}</div>
<div class="overall-header-right" />
</div>
</div>
</template>
<script>
import { reactive, toRefs } from 'vue'
import scrollReveal from 'scrollreveal'
export default {
name: 'Home',
components: {
},
setup () {
// 1.reactive()声明数值
const ret = reactive({
age: 22
name: '你好'
})
// 2.通过toRefs()结构声明的数值
const retOut = toRefs(ret)
// 3.记得通过扩展运算符导出
return { ...retOut }
}
}
</script>
- 可以设置多个
reactive()
但是用toRefs()
解构的数据只能存在一个
// 第一个reactive()
const data = reactive({
// 声明 scrollReveal组件
scrollReveal: scrollReveal(),
// 打字机内容
typewriter: res.my_typewriter,
})
// 第二个reactive()
const state = reactive({
// 顶部显示的状态位
header: 0
})
// 结构导出 只能结构一个reactive() 不能结构多个
const dataOut = toRefs(data)
// 结构导出 只能结构一个reactive() 不能结构多个
return { ...dataOut, state, gotoCV }
# 父子传值场景
- toRefs 用于将响应式对象转换为结果对象,其中结果对象的每个属性都是指向原始对象相应属性的ref。
- 常用于es6的解构赋值操作,因为在对一个响应式对象直接解构时解构后的数据将不再有响应式,而使用toRefs可以方便解决这一问题。
- toRefs后的ref数据如果是复杂类型数据时,不是原始数据的拷贝,而是引用,改变结果数据的值也会同时改变原始数据
- 作用其实和 toRef 类似,只不过 toRef 是对一个个属性手动赋值,而 toRefs 是自动解构赋值。
演示代码
import { defineComponent, toRefs } from 'vue'
export default defineComponent({
// 1. 接收父组件传来的参数
props: [title],
setup (props) {
// 2. 使用了解构赋值语法创建了变量myTitle
const { myTitle } = toRefs(props)
// 3. 内部使用数据的时候 必须要加value
console.log(myTitle.value)
// 4. return的时候 不需要带.value
return { myTitle }
}
})
toRef()
就不介绍了 和上面一样
# 组合API toRaw()
- vue3的响应式数据是通过
porxy
的方式 实现的响应式数据 那我我们有时候可能不需要响应式数据 比如一些固定的数据结构 那我们可以通过toRaw()
来临时把数据变成固定的普通对象 - 这是一个“逃生舱”,可用于临时读取数据而无需承担代理访问/跟踪的开销,也可用于写入数据而避免触发更改。不建议保留对原始对象的持久引用。请谨慎使用。
<script setup>
import { , reactive, toRaw } from 'vue
// 声明一个reactive响应式数据
const content = reactive({
foo: {}
})
// 让指定数据不使用porxy方式进行代理 让其不具备响应式 成为普通对象
const demo = () => {
const scene = toRaw(content.foo)
console.log(scene) // 不具备响应式的普通对象
}
</script>
- 适用于
three.js
框架中的某些特定不修改的数据结构 例如:Scene()场景对象
Mesh()网格模型
# 组合API shallowReactive()
浅层代理
shallowReactive()
使用和reactive()
使用方法一样 都可以储存任意类型数据 但何其不一致的是shallowReactive()
是浅层代理 对于深层数据不进行代理(比如对象包着对象)property
的值会被原样存储和暴露 并且他的代理方式并非proxy
- 该API适合一些不需要代理的对象 比如某些
数据结构
在three.js中就需要使用该种代理 来代理一些声明的数据结构 详情 proxy
会对性能有一些要求 我们可以通过该API 减少响应式的深度监测。
使用案例
- 点击涨薪,薪酬的数值不会递增。因为
salary
是person
的job
的属性,而shallowReactive
只能处理浅层次的响应式,即只能处理对象最外层属性的响应式。
<template>
<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, toRef} from "vue";
export default {
name:"Demo",
setup(){
let person = shallowReactive({
name:"张三",
age:18,
job:{
salary:30
}
})
return {
name:toRef(person,"name"),
age:toRef(person,"age"),
job:toRef(person,"job")
}
}
}
</script>
- 可以看到 深层的对象并不具备响应式 只有浅层的对象具备 所以叫做
浅层代理
# 组合API shallowRef
处理基本类型的响应式
shallowRef()
和Ref()
使用方法一样 但他只能代理基本理性的数据 不能处理对象类型的响应式。- 如果有一个对象数据, 后面会产生新的对象来替换 而非进行修改 就可以使用
shallowRef()
使用案例
- 点击点我加1,
sum
值递增。点击修改信息,obj
的msg
属性没有变化。
<template>
<h2>当前和为:{{sum}}</h2>
<button @click="sum++">点我加1</button>
<hr>
<h2>当前信息为:{{msg}}</h2>
<button @click="msg+='!'">修改信息</button>
</template>
<script>
import {shallowRef, toRef} from "vue";
export default {
name:"Demo",
setup(){
let sum = shallowRef(0);
let response = shallowRef({
msg:"你好"
})
return {
sum,
msg:toRef(response.value,"msg")
}
}
}
</script>
- 可以看到 代理的对象数据不具备响应式 这是因为
shallowRef()
只能处理基本类型的响应式,不能处理对象类型的响应式。
# 响应式数据使用规范
- 尽量不要混着用,
reactive
和ref
选一种,toRef
和toRefs
选一种,不然代码会很乱。 - 我喜欢
reactive
和toRefs
搭配使用
# 组合API computed
计算属性
目标:掌握使用computed函数定义计算属性
Vue中导入方法 需要用 import按需加载 才可以在
setup()
里面使用该方法计算属性不需要设置响应式数据(
ref
toref
数据本身就是动态计算的)计算属性的原数据被修改时候 会触发计算属性 同步更新新的数据(
setup()
只能触发一次) 相当于响应式计算属性不支持异步(掉接口)只支持同步
async
和promise
computed
函数,是用来定义计算属性的,计算属性不能修改。- 计算属性自带执行 无需放在生命周期中调用 写完即可
计算属性如果在模板中调用 需要设置判断 因为计算属性有延迟 所有需要判断 否则会报错1
计算属性不可以修改原来的数据 比如
push()
进去一个新数据 如果需要修改原数据 需要创建一个新的变量 接收修改后的数据- 计算属性接的值 如果调用 需要
变量名.value
// 创建一个计算属性 并且设置变量接收 const list = computed(() => { }) // 如果想调用计算属性接收的变量 需要.value list.value
- 计算属性接的值 如果调用 需要
演示例子
<template>
<div class="container">
<div>今年:{{age}}岁</div>
<div>后年:{{newAge}}岁</div>
</div>
</template>
<script>
import { computed, ref } from 'vue'
export default {
name: 'App',
setup () {
// 1. 计算属性:当你需要依赖现有的响应式数据,根据一定逻辑得到一个新的数据。
const age = ref(16)
// 得到后年的年龄
const newAge = computed(()=>{
// 该函数的返回值就是计算属性的值
return age.value + 2
})
return {age, newAge}
}
}
</script>
# computed
计算属性 高级使用方法
- 计算属性的高级写法
get()
和set()
无需设置return
get()
进行普通的计算流程set(v)
在get()
处理前修改需要计算数据 v是接收其数据
演示例子
<template>
<!-- 模板中不要求有唯一的根节点 -->
<div id="nav">
<div>我的初始值{{ret}}</div>
<div>我的更新值{{newRet}}</div>
<button @click="changeN">点我</button>
</div>
</template>
<script>
// 导入子组件
// 导入Vue3的生命周期
import { computed, ref } from '@vue/runtime-core'
export default {
setup () {
// 设置一个要修改的常量
const ret = ref(1)
const newRet = computed({
// 计算属性的高级用法
get () { // get()是普通的计算处理数据
return ret.value + 1
},
set (v) { // set(v)在get()处理前修改需要计算数据 v是接收其数据
ret.value = v
}
})
// 点击按钮 把计算属性的数据进行修改
const changeN = () => {
ret.value = 18
}
// 返回数据
return { changeN, ret, newRet }
}
}
</script>
目的:让计算属性支持双向数据绑定(可以更新计算属性的值,其实就是调用set方法)
总结:计算属性两种用法
- 给computed传入函数,返回值就是计算属性的值
- 给computed传入对象,get获取计算属性的值,set监听计算属性改变。
# 组合API watch()
函数方法 监听数据
目标:掌握使用watch函数定义侦听器
- Vue中导入方法 需要用 import按需加载 才可以在
setup()
里面使用该方法 watch()
监听ref
简单数据时候初始值不能为null 可以用reactive()
来设置null
- 定义计算属性:
watch()
是函数方法,是用来定义侦听器的 监听数据是否被修改watch()
有两个参数 参数1是修改后的数据 参数2是修改前的数据
- Vue中
watch()
里面的 四种方法和两种模式- 监听
ref()
定义的响应式数据 (监听简单数据)- 数组方法可以监听多个
ref()
数据
- 数组方法可以监听多个
- 监听
reactive()
定义的响应式数据 (监听复杂对象数据)- 监听
reactive()
定义的响应式数据,某一个属性
- 监听
- 函数方式监听数据
- 深度监听
deep: true,
- 默认执行
immediate: true
- 深度监听
- 监听
- 组件首次加载数据时,立刻触发一次
immediate: true
# watch()
函数方式 监听数据 (推荐)
- 函数方法监听数据 默认不支持深度监听 需要手动设置
deep: true,
- 函数方式监听指定数据 有两个属性
- 侦听对象中的子属性
deep: true,
- 组件首次加载数据时,立刻触发一次
immedate: true
- 侦听对象中的子属性
演示例子
// 监听的参数
const obj = reactive({
uname: 'lisi',
friend: {
info: 'hello',
ages: 13
}
})
// 函数方式 监听数据 watch(() => 需要监控的数据, (新的值, 旧的值) => { 进行的操作 })
watch(() => obj.friend, (newD, oleD) => {
// 设置watch()监听简单数据
// 参数1 是监听的修改后的数据
// 参数2 是监听的修改前的数据
console.log(newD)
},{
// 侦听对象中的子属性 (默认不知道是啥)
deep: true,
// 组件首次加载数据时,立刻触发一次(默认false)
immediate: true
}) // 组件首次加载数据时,立刻触发一次 (类似生命周期)
# watch()
监听ref()
定义的简单数据
- 监听
ref()
定义的简单响应式数据- 适合监听里面的数据
ref()
的简单数据
- 适合监听里面的数据
watch(监听数据,(新数据参数,旧数据参数)=>{ })
- 组件首次加载数据时,立刻触发一次
immediate: true
watch()
监听ref
简单数据时候初始值不能为null 可以用reactive()
来设置null
演示例子
<template>
<!-- 模板中不要求有唯一的根节点 -->
<div id="nav">
<div>我的初始值{{rets}}</div>
<button @click="changeRet">点我</button>
</div>
</template>
<script>
// 导入子组件
// 导入Vue3的组件
import { ref, watch } from 'vue'
export default {
setup () {
// 设置一个简单常量
const rets = ref(0)
// 设置一个自增函数方法 改变数据
const changeRet = () => {
// 给数据点击一次 +1
rets.value += 1
}
// 设置watch()监听简单数据
// 参数1 是监听的修改后的数据
// 参数2 是监听的修改前的数据
watch(rets, (newT,oleT) => {
console.log(newT,oleT)
}, { immediate: true }) // 组件首次加载数据时,立刻触发一次 (类似生命周期)
// 把数据return返回出去
return { rets, changeRet }
}
}
</script>
# watch()
监听ref()
多个定义的简单数据
- 数组方式监听多个
ref()
简单数据- 设置的数组中 只要有一个
ref()
设置的数据变化 就会监听到
- 设置的数组中 只要有一个
- 组件首次加载数据时,立刻触发一次
immediate: true
演示例子
<template>
<!-- 模板中不要求有唯一的根节点 -->
<div id="nav">
<div>{{rets}}</div>
<div>{{retx}}</div>
<button @click="changeRet">点我</button>
</div>
</template>
<script>
// 导入子组件
// 导入Vue3的组件
import { ref, watch } from 'vue'
export default {
setup () {
// 设置两个简单ref()数据
const rets = ref('张三')
const retx = ref(16)
// 设置一个方法 修改两个简单ref()的数据
const changeRet = () => {
// 修改对象里面的数据
rets.value = '李四'
retx.value = 18
}
// 设置watch()监听 使用数组的方法 监听ref()的数据
// 参数1 是监听的修改后的数据
// 参数2 是监听的修改前的数据
watch([retx, rets], (newT,oleT) => {
// 监听所有的修改前 修改后的数据
console.log(newT,oleT)
// 监听指定的修改后的数据
console.log(oleT[0])
console.log(oleT[1])
// 监听指定的修改后的数据
console.log(newT[0])
console.log(newT[1])
}, { immediate: true }) // 组件首次加载数据时,立刻触发一次 (类似生命周期)
// 把数据return返回出去
return { rets, retx, changeRet }
}
}
</script>
# watch()
监听reactive()
定义的复杂对象
- 监听
reactive()
定义的复杂响应式所有数据(监听所有复杂对象数据)- 如果监听一整个对象 那么里面所有的值只要变化 就会检测到
- 可以监听指定的数据对象中的数据
- 组件首次加载数据时,立刻触发一次
immediate: true
演示例子
<template>
<!-- 模板中不要求有唯一的根节点 -->
<div id="nav">
<div>我的初始值{{rets.age}}</div>
<button @click="changeRet">点我</button>
</div>
</template>
<script>
// 导入子组件
// 导入Vue3的组件
import { reactive, watch } from 'vue'
export default {
setup () {
// 设置一个对象数据
const rets = reactive({
uname: 'lisi',
age: 12,
friend: {
info: 'hello'
}
})
// 设置一个修改对象数据的方法
const changeRet = () => {
// 修改对象里面的数据
rets.age += 1
}
// 设置watch()监听对象里面的数据
// 参数1 是监听的修改后的数据
// 参数2 是监听的修改前的数据
watch(rets, (newT,oleT) => { // 可以rets.来监听指定的数据对象中的数据
console.log(newT,oleT)
}, { immediate: true }) // 组件首次加载数据时,立刻触发一次 (类似生命周期)
// 把数据return返回出去
return { rets, changeRet }
}
}
</script>
- 监听
reactive()
定义的复杂响应式的某条数据(监听一个)- 直接在 监听的两个参数后面设置监听的指定属性值即可
演示例子
watch(rets, (oleT, newT) => {
// 监听对象里面的所有数据
console.log(newT,oleT)
// 监听对象里面指定的数据
console.log(oleT.age, newT.age)
})
- 监听对象里的对象数据
- 如果监听的对象里面 还有一层数据 可以一环套一环的监听
演示例子
<template>
<!-- 模板中不要求有唯一的根节点 -->
<div id="nav">
<div>我的初始值{{rets.friend.ages}}</div>
<button @click="changeRet">点我</button>
</div>
</template>
<script>
// 导入子组件
// 导入Vue3的组件
import { reactive, watch } from 'vue'
export default {
setup () {
// 设置一个对象数据
const rets = reactive({
uname: 'lisi',
friend: {
info: 'hello',
ages: 13
}
})
// 设置一个修改对象数据的方法
const changeRet = () => {
// 修改对象里面的数据
rets.friend.ages += 1
}
// 设置watch()监听对象里面的数据
// 参数1 是监听的修改后的数据
// 参数2 是监听的修改前的数据
watch(rets.friend, (newT,oleT) => {
console.log(newT,oleT)
}, { immediate: true }) // 组件首次加载数据时,立刻触发一次 (类似生命周期)
// 把数据return返回出去
return { rets, changeRet }
}
}
</script>
总结: 掌握watch的各种用法。
- 侦听简单类型数据
- 侦听对象类型数据
- 侦听多个数据的变化
- 侦听某个属性的变化
# 组合API ref()
直接操作Dom节点
目标:掌握使用ref属性绑定DOM或组件
获取DOM或者组件实例可以使用ref属性,写法和vue2.0需要区分开
ref操作单个DOM元素----Vue3的规则
- 操作Dom节点 需要设置一个变量实现绑定Dom节点
const uname = ref()
操作Dom节点例子
- 操作Dom节点 需要设置一个变量实现绑定Dom节点
- 注意:如果你想操作dom 需要
.value
使用 必须把 设置的变量return
出去 - Vue2 操作Dom
this.$refs.example3.start();
- Vue3 操作Dom
example3.value.start()
必须.Value
<template>
<div>
<!-- 3、在模板中绑定ref对象 -->
用户名<input type="text" ref='uname'>
<button @click='handleClick'>点击</button>
<hr>
<ul>
<!-- Vue3中不可以直接这样批量绑定ref -->
<li ref='fruits' :key='index' v-for='(item, index) in list'>{{item}}</li>
</ul>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'App',
setup () {
// 1、创建一个引用对象 (获取
const uname = ref()
const handleClick = () => {
// 获取DOM元素的方式 uname.value
console.log(uname.value)
}
return {
// 2、把ref对象提供给模板
uname,
handleClick
}
}
}
</script>
<style lang="less"></style>
:ref
操作 v-for循环遍历的Dom数据(多个Dom元素)- Vue3多个操作多个ref需要动态绑定
:ref
- 设置一个空数组 储存获取到的循环遍历里面的数据 然后再单独获取
- Vue3中批量绑定
:ref
需求提供一个方法 把获取到的多个ref数据储存起来 innerHTML
获取:ref
标签里面的对应数据
- Vue3中批量绑定
- Vue3多个操作多个ref需要动态绑定
<template>
<div>
<!-- 0、在模板中绑定ref对象 -->
用户名<input type="text" ref='uname'>
<button @click='handleClick'>点击</button>
<hr>
<ul>
<!-- Vue3中绑定ref需要设置动态绑定 :ref -->
<li :ref='setDom' :key='index' v-for='(item, index) in list'>{{item}}</li>
</ul>
</div>
</template>
<script>
import { ref, reactive, toRef } from 'vue'
export default {
name: 'App',
setup () {
const obj = reactive({
// 1. 声明的数组数据
list: ['apple', 'orange', 'peach']
})
const list = toRef(obj, 'list')
// 2. Vue3中批量绑定ref需求提供一个方法 把获取到的多个ref数据储存起来
const fruits = []
const setDom = (el) => {
// 参数el是其中一个DOM元素(其中的一个数据)
// 3. 把多个ref获取的单独数据储存到数组里
fruits.push(el)
}
// 4. 创建一个引用对象
const uname = ref(null)
const handleClick = () => {
// 5. 获取DOM元素的方式 uname.value
// console.log(uname.value.value)
// innerHTML获取标签里面的数据(ipnut获取数据用的是value)
console.log(fruits[1].innerHTML)
}
return {
// 6、把ref对象提供给模板
uname,
list,
setDom,
handleClick
}
}
}
</script>
<style lang="less"></style>
# 组合API 父子传值
目标:掌握使用
props
选项和emits
选项完成父子组件通讯
- 导入组件 依旧需要 components: { }
- 子组件接收
props
父组件传来的值 需要设置toRef
或toRefs
把接收到的数据 变为响应式(不可以用ref
)
# 父组件 传 子组件
- 父组件给子组件传递数据 子组件用
props
接收
父组件传递数据
<template>
<div class="container">
<h1>父组件</h1>
<p>{{money}}</p>
<hr>
<Son :money="money" />
</div>
</template>
<script>
import { ref } from 'vue'
import Son from './Son.vue'
export default {
name: 'App',
components: {
Son
},
// 父组件的数据传递给子组件
setup () {
const money = ref(100)
return { money }
}
}
</script>
子组件接收数据
- 子组件需要先用
props
接收父组件传来的数据
<template>
<div class="container">
<h1>子组件</h1>
<p>{{money}}</p>
</div>
</template>
<script>
import { onMounted } from 'vue'
export default {
name: 'Son',
// 子组件接收父组件数据使用props即可
props: {
money: {
type: Number,
// 默认传递数据为0
default: 0 // false
}
},
// props 获取父组件的数据
// context 用来调用emit 触发自定义事件的函数(这里用于 子组件向父组件传值)
setup (props,context) {
// 获取父组件数据money
console.log(props.money)
}
}
</script>
总结:父组件向子组件传值,类型检测和Vue2相同,模板中访问props
中的数据也是一样的
注意:js代码中得到父组件传递的值通过setup()
函数的参数一props
获取。
# 子组件 传 父组件
- 通过
setup()
函数的参数二context
对象中emit
方法触发子向父传值的事件。
子组件传递数据
- 通过
setup()
函数的参数二context
想父组件传递数据
<template>
<div class="container">
<h1>子组件</h1>
<p>{{money}}</p>
+ <button @click="changeMoney">花50元</button>
</div>
</template>
<script>
import { onMounted } from 'vue'
export default {
name: 'Son',
// 子组件接收父组件数据使用props即可
props: {
money: {
type: Number,
default: 0
}
},
// props 获取父组件的数据
// context 用来调用emit 触发自定义事件的函数(这里用于 子组件向父组件传值)
+ setup (props, context) {
// 获取父组件数据money
console.log(props.money)
// 向父组件传值
+ const changeMoney = () => {
// 消费50元
// 通知父组件,money需要变成50
+ context.emit('change-money', 50)
+ }
+ return {changeMoney}
}
}
</script>
父组件接收数据
<template>
<div class="container">
<h1>父组件</h1>
<p>{{money}}</p>
<hr>
+ <Son :money="money" @change-money="updateMoney" />
</div>
</template>
<script>
import { ref } from 'vue'
import Son from './Son.vue'
export default {
name: 'App',
components: {
Son
},
// 父组件的数据传递给子组件
setup () {
const money = ref(100)
+ const updateMoney = (newMoney) => {
+ money.value = newMoney
+ }
+ return { money , updateMoney}
}
}
</script>
总结:通过setup函数的参数二context对象中emit方法触发子向父传值的事件。
# 语法糖 v-model:
父子组件之前相互传值
目标:掌握vue3.0的v-model语法糖原理
- 在Vue3中
v-model:
的用法 类似于Vue2中的.sync
方法 父组件 子组件双向绑定数据 省去@
- 在vue3中
v-model:
语法糖的拆分:<Son :modelValue="msg" @update:modelValue="msg=$event" />
v-model:
有默认值modelValue
默认值绑定的话 不需要设置名称 但是子组件传值的时候需要写'update:modelValue'
- 默认导出的时候 不需要用
props
接收
- 默认导出的时候 不需要用
v-model
命名传值 子组组件传值的时候需要写update:命名
- 命名导出的时候 需要用
props
接收
- 命名导出的时候 需要用
- 子组件接收
props
父组件传来的值 需要设置toRef
或toRefs
把接收到的数据 变为响应式(不可以用ref
)
Vue2的.sync修饰符双向绑定:
- 在vue2.x的时候
.sync
除去v-model实现双向数据绑定的另一种方式
<!-- <Son :money='money' @update:money="fn" /> -->
<Son :money.sync='money' />
Vue3的v-model双向绑定:
- 在vue3.0的时候,使用
v-model:money="money"
即可代替Vue2的.sync
<!-- <Son :money="money" @update:money="updateMoney" /> --><Son v-model:money="money" />
v-model 默认名称传值
- 使用
v-model
默认名称 进行父子组传值- 父组件向子组传递数据 并且是默认名称进行传值
v-model
- 父组件向子组传递数据 并且是默认名称进行传值
<!-- 弹窗的组件 state.showDialog是要传的值 -->
<checkProject v-model="state.showDialog" />
// 状态位
const state = reactive({
// 详情页弹窗
showDialog: false
})
return { state }
- 子组件 接收父组件传来的值 使用
update
进行双向绑定- 父组件通过
v-model
默认名称进行父子传值 所以需要设置update:modelValue
来向父组件传递数据 - 父组件通过
v-model
默认名称进行父子传值 所有接收数据 也要通过'modelValue'
来获取父组件传来的数据 - 子组件接收
props
父组件传来的值 需要设置toRef
或toRefs
把接收到的数据 变为响应式(不可以用ref
) - 默认导出的时候 不需要用
props
接收
- 父组件通过
<template>
<el-dialog v-model="data.showDialog" title="提示">
<span>这是一段信息</span>
<!-- Vue3插槽 -->
<template #footer>
<el-button type="primary" @click="handleClose">关 闭</el-button>
</template>
</el-dialog>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'CheckProject',
setup (props, context) {
// 储存接收的值
const data = reactive({
showDialog: props.showDialog
})
// 关闭弹窗方法
const handleClose = () => {
// 关闭弹窗 默认传值 update:modelValue
context.emit('update:modelValue', false)
}
return { handleClose, data }
}
}
</script>
v-model 命名传值
- 使用
v-model
命名 进行父子组传值- 父组件向子组传递数据 并且是命名进行传值
v-model:名称
- 父组件向子组传递数据 并且是命名进行传值
<template>
<div>
父组件设置的值 {{ret}} {{rets}}
<hr>
<!-- 给子组件传递数据 -->
<!-- v-model可以双向绑定父组件子组件 类似于 .sync -->
<son v-model:ret='ret' v-model:rets='rets'>
</son>
</div>
</template>
<script>
import { ref } from '@vue/reactivity' // 引入子组件
import son from './views/model/son.vue'
export default {
components: {
// 导入子组件
son
},
setup () {
// 设置传递子组件的数据
const ret = ref(123)
const rets = ref('你好')
return { ret, rets }
}
}
</script>
- 子组件 接收父组件传来的值 使用
update
进行双向绑定- 子组件接收
props
父组件传来的值 需要设置toRef
或toRefs
把接收到的数据 变为响应式(不可以用ref
) - 命名导出的时候 需要用
props
接收
- 子组件接收
<template>
<div>
子组件内容 {{ret}}
<button @click="changeRet">点击改变</button>
</div>
</template>
<script>
export default {
props: {
// 接收父组件传来的数据
ret: {
require: false
},
rets: {
require: false
}
},
setup (props, context) {
const changeRet = () => {
// 向父组件传值 修改父组件的数据 update双向绑定
context.emit('update:ret', 100)
context.emit('update:rets', '大家好')
}
return { changeRet }
}
}
</script>
总结:
vue3.0封装组件支持v-model的时候,父传子
:modelValue
子传父@update:modelValue
Vue3中的v-model的新的用法可以替代Vue2中的sync修饰符
v-model双向绑定组件属性的规则:
- 父组件的绑定 v-mode:属性名称=‘数据名称’
- 子组件触发更新的动作: context.emit('update:属性名称', 要更新的数据)
# 组合API provide
inject
依赖注入(相互传值类似Vuex)
目标:掌握使用provide函数和inject函数完成后代组件数据通讯
使用场景:有一个父组件,里头有子组件,有孙组件,有很多后代组件,共享父组件数据。
- 注意: 依赖注入的数据 需要用
.value
来读取数据 - 依赖注入 类似以Vuex 可以父组件可以直接向其他组件传递数据 直接可以接收父组件传递的数据
原组件设置方法 进行传递
- 父组件可以直接把数据传递给 子组件 孙组件 无需父传子方法传递数据(类似Vuex)
- 可以传递数据 和 函数方法
<template>
<div>
传递给其他组件的数据 {{ret}}
<sunzi></sunzi>
</div>
</template>
<script>
// 导入Vue3的方法
import sunzi from '@/views/model/sunzi'
import { ref } from '@vue/reactivity'
import { provide } from '@vue/runtime-core'
export default {
components: {
// 加载其他组件
sunzi
},
setup () {
// 设置要传递的数据
const ret = ref(123)
// 设置要传递的方法
const getRet = (removeRet) => {
ret.value = ret.value - removeRet
}
// 传递数据给其他组件
provide('ret', ret)
// 传递方法给其他组件
provide('getRet', getRet)
// 返回数据
return { getRet, ret }
}
}
</script>
子组件接收原组件传来的数据
- 接收原组件传来的数据和方法
- 通过原组件传来的方法 修改原组件的数据
<template>
<div>
接收传来的值 并且修改{{ret}}
<button @click="remove">减少20</button>
</div>
</template>
<script>
import { inject } from '@vue/runtime-core'
export default {
setup () {
// 接收传递的数据
const ret = inject('ret')
// 接收传递的方法
const getRet = inject('getRet')
const remove = () => {
// 给接收的方法 设置子组件数据 并传回原组件进行修改
getRet(20)
}
return { ret, remove }
}
}
</script>
总结:
provide
函数提供数据和函数给后代组件使用- 提供的函数方法可以用来 计算传递过去的原数据 进行原数据修改
inject
函数给当前组件注入provide
提供的数据和函数
注意:传递数据的目的是从上向下传递数据;传递函数的目的是为了从下向上传递数据。
# 创建Vue3的 全局实例化
- Vue3脚手架换进下 在
main.js
文件夹里面 设置全局方法createApp(App)
是Vue3的全局方法实例
- Vue3的全局 api方法官方示例 官方Vue全局方法 (opens new window)
- Vue2的全局方法 Vue3也可以用 但是需要从
createApp(App)
调用
- Vue2的全局方法 Vue3也可以用 但是需要从
- 全局实例化 通常被用于实例化 插件 和 自定义方法
main.js 设置全局方法
// 这种导入api的方法可以减少打包后的体积(按需导入)
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// 设置Vue3的全局方法实例
// 代替了Vue2的 Vue.的全局方法
const app = createApp(App)
// 全局过滤器方法
app.filter({})
// 全局混入mixin(Vue3 只推荐全局)
app.mixin({})
// Vue3的挂载实例化对象的方法 .use()方法用于配置插件
app.use(store).use(router).mount('#app')
# Vue3自定义插件机制 install
目标:熟悉Vue的插件机制(针对Vue核心规则的一种扩展机制)
- Vue3配置全局插件是 app. (Vue2是 Vue.)
- Vue3设置插件的时候 设置插件的Vue组件需要 设置
name
属性
# 设置Vue3全局组件 install
第一步 把写好的Vue组件 设置为插件
把写好的Vue组件 在一个文件中 统一设置为插件
设置插件的Vue组件需要 设置
name
属性app.component
设置插件的名称 需要和 组件内部设置的name名称一致
// 导入需要设置为插件的Vue组件
import XtxSkeleton from './xtx-skeleton.vue'
// 自定义一个插件导出
export default {
install (app) {
// 配置全局插件 app.component 设置插件的名称 需要和 组件内部设置的name名称一致
// app表示Vue全局插件的实例对象 (Vue2是Vue. Vue3是app.)
app.component(XtxSkeleton.name, XtxSkeleton) // (导入组件的名称的name名.name,插件的名称)
}
}
第二步 实例化插件 让其能在Vue组件中使用
- 入口文件一般是
main.js
- 通过
.use(插件名称)
实例化该插件
// 导入实例化的Vue3
import { createApp } from 'vue'
import App from './App.vue'
// 导入自定义插件的配置文件 让其实例化 可在组件内使用
import XtxUI from './components/library/index'
// 在createApp(App)后面 .use()导入自定义的插件
createApp(App).use(XtxUI).mount('#app')
第三步 实例化插件后 无需导入 直接在Vue组件中使用
- 无需导入 直接在
template
模板中调用插件即可(导入配置的插件名称)
<template>
<div class="home-banner">
<!-- 直接导入插件即可 可以在里面绑定参数 -->
<XtxSkeleton />
</div>
</template>
注意:
如果需要给插件传递参数 通常会动态绑定数据`:` ( 动态绑定基本类型的传入插件的数据 可保证插件需求的数据类型)
# 批量导入Vue全局组件方法
- 适用于 所有插件都集合在一个文件夹 (index.js里面)
- 通过
require
方法筛选插件 然后importFn
进行批量导入插件方法 然后批量component
实例化Vue插件 - 无需
import
导入
// -------------------------------------- 批量导入Vue插件
// 参数一:从哪个目录中读取文件
// 参数二:是否读取子目录中的文件:true读取子目录,false不读取子目录
// 参数三:读取文件的匹配规则
const importFn = require.context('./', false, /\.vue$/)
// 自定义一个插件方法导出
export default {
install (app) {
// 批量注册 -------------------------------------------------------------------
// 自动化批量注册全局组件
importFn.keys().forEach(componentPath => {
// componentPath表示其中一个组件的路径
// 根据路径导入组件
// 返回值component表示组件的实例对象
const component = importFn(componentPath).default
// 注册全局组局
app.component(component.name, component)
})
}
}
# Vue3的 mixin
混入
总结: 在vue2.0中一些可复用的逻辑可以使用mixin来封装,防止相同逻辑的代码在组件中存在冗余。vue3.0的组合API很好的解决了这个问题,就不在推荐使用mixin了。
注意:实际上组合API的模式替代了mixin的应用场景。
mixin 全局混入的特性是 所有的其他组件都可以使用
Vue3 如果想在生命周期中触发全局混入 不能用Vue3的声明周期 需要写Vue2的
created()
声明周期中Vue3中有组合api 不推荐使用 局部混入 全局混入可以使用
全局混入例子
- 脚手架环境下
main.js
文件 设置全局混入
// main.js设置全局混入
// 这种导入api的方法可以减少打包后的体积(按需导入)
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// 设置Vue3的全局方法实例
// 代替了 Vue2的 Vue.的全局方法
const app = createApp(App)
// 设置全局混入mixin
app.mixin({
// 设置全局混入的methods方法
methods: {
sayHello () {
console.log('你好')
}
}
})
// Vue3的挂载实例化对象的方法 .use()方法用于配置插件
app.use(store).use(router).mount('#app')
- Vue文件中 导入全局混入方法
<template>
<div>
<!-- 直接给点击事件绑定混入 点击后触发混入 -->
<button @click="sayHello">全局混入按钮</button>
</div>
</template>
<script>
export default {
// Vue3 在声明周期中导入混入方法 (不要用Vue3的声明周期方法)
created () {
this.sayHello()
},
setup () {
}
}
</script>
总结: 在vue2.0中一些可复用的逻辑可以使用mixins来封装,防止相同逻辑的代码在组件中存在冗余。但是vue3.0的组合API很好的解决了这个问题,就不在推荐使用mixins了。
注意:实际上组合API的模式替代了mixin的应用场景。
# Vue3 promise
掉接口方法
- Vue3
promise
的掉接口 赋值方法( 获取服务器的数据)
promise
掉接口方法 (不传参)
<script>
// 导入Vue3 ref方法
import { ref } from 'vue-demi'
// 导入获取数据的接口api
import { findBanner } from '../../../api/home'
export default {
name: 'HomeBanner',
setup () {
// 创建一个ref响应式 接收服务器的数据 (ref可以存数组)
const slides = ref([])
// 储存获取到的服务器数据
findBanner().then(ret => {
// 如果获取成功 把服务器的数据 储存起来
slides.value = ret.result
}).catch((err) => {
// 如果获取失败 打印错误
console.log(err)
})
return { slides }
}
}
</script>
promise
掉接口方法 (传参)
// promise 掉接口方法 (传参)
findBanner(id:10).then(ret => {
// 如果获取成功 把服务器的数据 储存起来
slides.value = ret.result
}).catch((err) => {
// 如果获取失败 打印错误
console.log(err)
})
return { slides }
}
}
总结:
- 基于
promise
方式 获取服务器的数据
# Vue3 v-slot
插槽设置
- 插槽,我们想象一下电脑的主板,主板上面有很多插槽,而每个插槽可能对应了不同的配置,那么在vue中,也可以这么来理解,各个插槽有各个插槽的功能,它们相互独立,可用于组件之间通信。
- Vue3中也支持
v-slot
并且支持其简写#
v-slot
和#
是具名插槽- 具名插槽 导入的时候 需要
<slot name="具名插槽名称" />
- 具名插槽 导入的时候 需要
<slot />
是默认插槽- 默认插槽 导入的时候 直接
<slot />
- 默认插槽 导入的时候 直接
在同一组件中 设置了一部分具名插槽 剩下的部分会自动为默认插槽
插槽例子
- 设置作用域插槽 (具名 和 默认)
<template>
<div class="home-new">
<HomePanel title="新鲜好物" sub-title="新鲜出炉 品质靠谱">
<!-- 具名插槽的数据 -->
<template #right>
<XtxMore path="/" />
</template>
<!-- 默认插槽的数据 -->
<ul class="goods-list">
<li v-for="item in goods" :key="item.id">
<RouterLink :to="`/product/${item.id}`">
<img :src="item.picture" alt="">
<p class="name ellipsis">{{item.name}}</p>
<p class="price">¥{{item.price}}</p>
</RouterLink>
</li>
</ul>
</HomePanel>
</div>
</template>
<script>
import { ref } from 'vue'
// 导入调用插槽的模板
import HomePanel from './home-pannel.vue'
import { findNew } from '@/api/home'
export default {
name: 'HomeNew',
// 使用导入的插槽模板
components: { HomePanel },
setup () {
const goods = ref([])
findNew().then(data => {
goods.value = data.result
})
return { goods }
}
}
- 在插槽组件中 导入具名插槽和默认插槽 进行数据加工
<template>
<!-- 好物推荐的整体 -->
<div class="home-panel">
<div class="container">
<div class="head">
<h3>{{ title }}<small>{{ subTitle }}</small></h3>
<!-- 具名插槽:用于填充右侧内容(好物推荐整体上面右侧的查看全部)-->
<slot name="right" />
</div>
<!-- 默认插槽: (好物推荐整体下面商品内容) -->
<slot />
</div>
</div>
</template>
# Vue3 的动画过度 Transition
目标: 知道vue中如何使用动画,知道Transition组件使用。
- Vue提供的一个标签
<Transition name="类名"></Transition>
把需要动画效果的内容 包住 让其拥有动画效果 - name 提供的样式绑定类名 需要绑定css样式 才能生效动画效果( 推荐绑定到全局样式 设置过度效果显示隐藏 )
- Vue动画有六个类(都需要设置)
- 离开前 离开后
- 显示前 显示后
- 动画前 动画后
- 多个transition使用不同动画,修改name属性即可。
- 动画过度会被缓存 相同的组件 只会执行一次动画 需要动态绑定
:key
让其变为不同组件
使用案例
- Vue模板绑定
<Transition name="类名"></Transition>
设置动画
<template>
<div>
<!-- 绑定动画模板 name是样式绑定类 -->
<Transition name="fade">
<!-- 这里填充需要动画的内容 -->
<div>
过度动画的效果
</div>
</Transition>
</div>
</template>
- css 动画效果设置
- 可以写在 style 但是推荐写在全局样式文件中 全局设置
- 中间过度细节 可以只设置 进入的时候
// 这里是Vue的过度动画效果(name名是fade)
//开始时候的效果(默认是 opacity: 1;)
.fade-enter-to,
.fade-leave-from {
// 设置为隐藏
opacity: 1;
}
// 中间的过度细节
.fade-enter-active, //进入的时候
.fade-leave-active // 离开的时候(通常不设离开样式)
{
position: absolute;
width: 100%;
// 设置动画过度显示效果
transition: opacity 0.5s 0.2s;
z-index: 1;
}
// 结束的时候
.fade-enter-from,
.fade-leave-to {
// 设置为显示
opacity: 0;
}
注意:多个transition使用不同动画,可以添加name属性,name属性的值替换v即可。
# 过度动画执行顺序 mode
- 默认情况下,进入动画和离开动画是同时进行的。
- 可以通过
mode
设置过度动画的执行顺序mode='out-in'
先离开后进入mode='n-out'
先进入后离开
<!-- 设置先离开后进入 -->
<transition name="class-name" mode="out-in">
<!-- 需要动画效果的内容 -->
<transition>
# Vu3 自定义实例化方法mounted
mounted自定义实例化方法介绍 (opens new window)
- Vue支持自定义插件 也支持自定义实例化方法
mounted
设置自定义方法后 需要install
导出自定义方法 (install
也可以导出插件)- 1导出方法后 通过 入口文件
use.()
实例化自定义方法 - Vue自定义实例化方法在模板使用时候需要
v-方法名称
- Vue2自定义实例化方法是
Vue.
Vue3自定义实例化方法是app.
- 代替
:src
动态绑定图片路径方法
# Vue3中的 Vue Router
- Vue3的 Vue Router 也需要按需导入 才能使用router的api
import { useRouter,useRoute } from 'vuex'
useRouter
通常用作跳转 是操作路由的方法useRoute
通常用来接收跳转参数 是获取路由信息的方法
- Vue Router 有两种传参跳转方式
query
方式: 会在地址中显示 刷新后参数不会消失 类似以GET
请求方式- 通过
path
方式进行跳转 需要携带跳转路由的path
地址
- 通过
params
方式: 不会在地址中显示 但是刷新后参数会消失 类似于POST
方式- 通过
name
方式进行跳转 需要携带跳转路由的name
地址
- 通过
- 注意:query传值跳转的
path
需要和你设置路由的配置文件path
一致 - 注意:params传值跳转的
name
需要和你设置路由的配置文件name
一致
# 普通跳转
- 不携带任何参数 就是普普通通的跳转
<script setup>
// 导入操作路由方法
import { useRouter } from 'vue-router'
// 注册操作路由方法
const router = useRouter()
// 跳转页面
const toGranary = () => {
router.push('/togranary')
}
</script>
# query传值跳转方式
- query方式跳转 可以不用写对象 用模板字符串简写传值跳转 两种都可以
- 注意:query传值的
path
需要和你设置路由的配置文件path
一致
<script setup>
// 导入操作路由方法
import { useRouter } from 'vue-router'
// 注册操作路由方法
const router = useRouter()
// 跳转页面
const toGranary = () => {
// 不简写的query跳转方式
router.push({
path: '/togranary',
query: {
id: 1
}
})
// 用模板字符串的跳转方式
router.push(`/togranary?id=${1}`)
}
</script>
- 接收
query
传来的路由参数 通过useRoute
方式获取route.query.属性名
- 当然
useRouter
方式也可以获取就是麻烦不推荐router.currentRoute.value.query.属性名
<script setup>
// 导入路由
import { useRoute, useRouter } from 'vue-router'
// 导入Vue组合API
import { onMounted } from 'vue'
// 注册useRoute
const route = useRoute()
// 注册useRouter
const router = useRouter()
onMounted(() => {
// 通过useRoute获取参数
console.log(route.query.id)
// 通过useRouter获取参数 十分的麻烦
console.log(router.currentRoute.value.query.id)
})
</script>
# params传值跳转方式
- params跳转方式 不能简写 需要写对象
- 注意:params传值跳转的
name
需要和你设置路由的配置文件name
一致
<script setup>
// 导入操作路由方法
import { useRouter } from 'vue-router'
// 注册操作路由方法
const router = useRouter()
// 跳转页面
const toGranary = () => {
// params跳转方式
router.push({
name: 'ToGranary',
// 隐式参数 不在页面路径显示 但是刷新就没啦
params: {
id: 1
}
})
}
</script>
- 通过
useRoute
和useRouter
接收参数
<script setup>
// 导入路由
import { useRoute, useRouter } from 'vue-router'
// 导入Vue组合API
import { onMounted } from 'vue'
// 注册useRoute
const route = useRoute()
// 注册useRouter
const router = useRouter()
onMounted(() => {
// 通过useRoute获取参数
console.log(route.params.id)
// 通过useRouter获取参数 十分的麻烦
console.log(router.currentRoute.value.params.id)
})
</script>
# 标签的路由传值
- 一级路由携带id配置动态路由
- 基于
<RouterLink>
跳转方式(类似于a标签) - 使用模板字符串传递动态路由参数
- 基于
<template v-if="item.children">
<!-- RouterLink跳转配置动态路由数据 使用模板字符串 -->
<RouterLink v-for="sub in item.children" :key="sub.id" :to="`/category/sub?id=${sub.id}`">
{{ 我是动态路由 }}
</RouterLink>
</template>
- 二级路由接收一级路由跳转传入的动态路由数据
- Vue3的 Vue Router 也需要按需导入 才能使用router的api
import { useStore } from 'vuex'
// 导入Vue router方法
import { useStore } from 'vuex'
export default {
setup () {
// 实例化Vue router方法
// route相当于之前的this.$route
const route = useRoute()
// 获取动态路由传来的id值
const id = route.params.id
}
}
# 切换路由后滚动到顶部
- 只要切换路由组件(切换地址) 就会默认滚到顶部(一级二级都会触发)
scrollBehavior ()
方法可以设置滚到顶部
// 设置Vue router实例化
const router = createRouter({
// 设置hash路径方式
history: createWebHashHistory(),
// 映射 router配置的路由(配置的路由组件)
routes,
// Vue router的切换路由 滚动到顶部
scrollBehavior () {
return {
// x: 0, y: 0 // vue2
// left控制页面水平方向滚动
// top控制垂直方向滚动
left: 0, top: 0
}
}
})
// 导出router实例化对象
export default router
# 处理跳转细节
目的: 在路由跳转的时候,优化跳转的细节。
大致需求:
- 通过
watch
监听变化 从而获取相应的数据 会导致非本级路由触发watch
(watch
无法判断是否是一级二级) - 切换到二级类目路由的时候也有ID,但是也触发了
watch
导致发送了请求,需要处理。 - 通过
indexOf
来判断是否存在二级路由 如果存在return
出去
案例: 处理watch 筛选二级路由
- 判断是否存在
/category/sub
二级路由 如果存在 筛选出去 不调用接口方法- 通过
indexOf
来判断是否存在二级路由 如果存在 return出去
- 通过
// 获取一级分类相关的商品数据
const goods = ref([])
// 只要路由参数params.id值发生变化,那么就会触发侦听(一级和二级分类都是动态路由)
watch(() => route.params.id, (newId) => { // 监控路由的变动 获取路由相应的数据
// 屏蔽掉二级分类路由参数的变化
console.log(route.params.id)
// 进行查询 如果查询到'/category/sub' 就让其return(查不到是-1 查到是1)
if (route.path.indexOf('/category/sub') !== -1) return
findTopCategory(newId).then(data => {
goods.value = data.result.children
})
}, {
// 页面加载时候 启动watch
immediate: true
})
**总结:**跳转的时候需要注意些细节
# Vue3的nextTick(()=>{})
Dom节点加载完毕后执行
- 当数据更新了,在dom中渲染完毕后,自动执行
nextTick()
里面的代码, - 在Vue3中 需要按需导入
nextTick
才可以使用 - 通常先置空数据(null) 然后通过
nextTick()
重新赋值 实现响应式数据- 保证之前的数据先清空,然后,再重新赋值新的数据
使用案例:
- 引入
import { nextTick } from 'vue'
- 使用
// 通过watch 监控是否切换路由 如果切换路由重新获取数据
watch(() => route.params.id, (newId) => {
// 只监控当前页面的路由
if (route.path !== '/product/' + newId) return
// 调用接口获取新的信息数据
findGoods(newId).then(data => {
// 先置空之前的数据
goodDetail.value = null
// 然后通过nextTick()组价加载完成后执行 获取数据
// 保证之前的商品数据先清空,然后,再重新赋值新的数据
nextTick(() => {
// 把新的信息数据储存
goodDetail.value = data.result
})
})
}, {
immediate: true
}
# Vue3 组合方式 拆分组件
- 如果一个组件中 有多个功能全部写在
setup()
里 不方便维护 所以把每个功能拆分出来 再导入 - 简化
setup()
中的代码,方便代码的重用和后期的维护 - 拆分的名称 和 导入的名称 不要一致 (不能重名)
无需传参的拆分
- 无需设置参数 直接拆分即可
<script>
// 把一个功能全部代码拆分
const useGoods = () => {
// 功能的全部代码(假设有)
}
export default {
name: 'XtxGoodsPage',
components: { GoodsRelevant, GoodsImage },
setup () {
// 导入拆分的代码
// 简化setup中的代码,方便代码的重用和后期的维护
const goodsDetail = useGoods()
// return出去导入的拆分代码
// 这里是template模板中需要的数据
return { goodsDetail }
}
}
</script>
需要传参的拆分
- 有时候需要获取父组件传来的值
props
和setup()
方法(字串符emit
)就需要传参
<script>
// 把一个功能全部代码拆分
// 接收拆分后的参数
const useGoods = (props,emit) => {
// 功能的全部代码(假设有)
}
export default {
name: 'XtxGoodsPage',
components: { GoodsRelevant, GoodsImage },
setup (props,{emit}) { // 结构赋值 emit
// 导入拆分的代码
// 如果需要传参 需要设置参数
const goodsDetail = useGoods(props,emit)
// return出去导入的拆分代码
// 这里是template模板中需要的数据
return { goodsDetail }
}
}
</script>
# Vue3 重置对象方法
- Vue2中 可以用
$options
来重置对象里面内容 - 在Vue3中不能使用
$options
所以需要遍历 来重置对象里面的数据- 通过
for in
来实现重置对象里面的数据
- 通过
重置对象方法案例
setup () {
// 需要重置的对象数据
const changeResult = reactive({
provinceCode: '',
provinceName: '',
cityCode: '',
cityName: '',
countyCode: '',
countyName: '',
fullLocation: ''
})
// 打开弹窗时,重置数据
const toggle = () => {
for (const key in changeResult) {
// key是属性名(键)
changeResult[key] = ''
}
}
}
# Vue3的深度选择器 :deep()
- 深度选择器通常作用于修改封装好组件的样式 覆盖组件之前的样式
- Vue3的深度选择器
:deep(类名)
// Vue3中深度选择器使用规则 :deep(类名)
:deep(.xtx-carousel) {
height: 380px;
}
# Vue3组件的控制component
- Vue.js是由多个组件(component)组合而成的
<component :is=''></component>
相当于有 直接导入组件 不是动态的 可以实现切换不同的组件(显示与隐藏):is
是动态绑定导入的组件名
- 通常可以用
component
来实现选项卡功能 - 此方法可以代替
v-if
来实现组件的显示与隐藏
点击后 动态切换组件
- 设置点击事件 携带需要切换的组件名称 实现点击后 切换相应的组件
- 切换原理: 点击后 获取传来的组件名参数 赋值给控制组件切换的变量 实现动态切换组件
<template>
<div>
<!-- 设置点击事件 携带需要切换的组件名称 实现点击后 切换相应的组件 -->
<a @click='toggle("GoodsDetail")' href="javascript:;">商品详情</a>
<a @click='toggle("GoodsComment")' href="javascript:;">商品评价</a>
<component :is='componentName'></component>
</div>
</template>
<script>
// 导入模拟要动态切换的组件
import GoodsDetail from './goods-detail.vue'
import GoodsComment from './goods-comment.vue'
export default {
name: 'GoodsTabs',
// 导入模拟切换的组件
components: { GoodsDetail, GoodsComment },
setup () {
// 当前组件的名称(默认显示的组件)
const componentName = ref('GoodsDetail')
// 点击后 获取传来的组件名参数 赋值给控制组件切换的变量 实现动态切换组件
const toggle = (name) => {
// 把点击携带的组件名赋值给 控制组件切换的变量
componentName.value = name
}
// 把控制组件切换的变量返回
return { toggle, componentName }
}
}
</script>
# Vue3在模板内设置样式 :style
- Vue3的模板 可以直接绑定
@click
事件 就算组件不支持@click
事件 也不需要像Vue2一样使用.native
强制设置 :style
可以直接在模板上 设置样式(行内样式 高权重) 事件监听也支持 相当于用JS来进行css样式设置:style
有两种写法动态写法- 通过
{}
来写 他的写法和JS修改样式写法一样 带有-
的css样式 必须变成驼峰式写法 用,
进行分割 - 通过模板字符串来写 模板字符可以使用单位拼接 也可以直接使用拼接好的值 用
;
进行分割
- 通过
简单案例
<template>
<div>
<!-- 通过{}来写 他的写法和JS修改样式写法一样 带有-的css样式 必须变成驼峰式写法 -->
<div :style="{maxWidth: 'widthValue',width: '100%'}" />
<!-- 通过模板字符串来写 模板字符可以使用单位拼接max-width: ${widthValue}px; 也可以直接使用拼接好的值 -->
<div :style="`max-width: ${widthValue};width: ${100}%`" />
</div>
</template>
// widthValue的值
widthValue: '400px',
# Vue3动态修改style v-bind
可以通过
v-bind
绑定css/html属性 动态修改指定的样式现在我们用起来,第一次使用
<style> v-bind
style v-bind
将span
变成红色
<template>
<span> 有开始循环了-开端 </span>
</template>
<script setup>
import { reactive } from 'vue'
const state = reactive({
color: 'red'
})
</script>
<style scoped>
span {
/* 使用v-bind绑定state中的变量 */
color: v-bind('state.color');
}
</style>
# 修改伪类选类选择器 ::aftert和::before中的content
- 修改
::aftert
和::before
中的content
属性需要在绑定的字符串值中提供引号- 可以通过
replace
替换方法删除模板中的引号
- 可以通过
<template>
<div class="button-box">
<button>{{ text.replace(/\'/g, '') }}</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const text = ref("'赛博朋克'")
</script>
<style lang="scss" scoped>
.button-box {
&::before {
content: v-bind('text');
}
}
</style>
# 声明缓存数组 v-memo
v-memo
是 Vue.js 3.2 版本新增的指令,它可以用于普通标签,也可以用于列表,结合 v-for 使用v-memo
仅供性能敏感场景的针对性优化,会用到的场景应该很少。渲染 v-for 长列表 (长度大于 1000) 可能是它最有用的场景- Table 表格组件在 Web 开发中的应用随处可见,不过当表格数据量大后,伴随而来的是性能问题:渲染的 DOM 太多,渲染和交互都会有一定程度的卡顿。
v-memo
可以帮助我们解决这个问题
- Table 表格组件在 Web 开发中的应用随处可见,不过当表格数据量大后,伴随而来的是性能问题:渲染的 DOM 太多,渲染和交互都会有一定程度的卡顿。
让页面性能更好 之前渲染超多数据 会卡顿 这次通过
v-memo
使用缓存的 vnode,它们指向同一个对象引用,直接返回,节约了后续执行 patch 过程的时间。防止卡顿
示例
- 记住一个模板的子树。元素和组件上都可以使用。该指令接收一个固定长度的数组作为依赖值进行记忆比对。如果数组中的每个值都和上次渲染的时候相同,则整个该子树的更新会被跳过。例如:
<!-- 假设listArr -->
<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">
<p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p>
<p>...more child nodes</p>
</div>
- 如果不在上面的代码中使用
v-memo
,selected 变量的每一次改变都会导致列表的完全重新渲染。新指令提供的缓存,允许只更新表达式item.id === selected
发生变化的行,也就是当某个项被选中或者取消时。 - 如果我们考虑一个有 1000 条数据的列表。使用上述代码的
v-memo
,可以为每一个变化节省998个条重新渲染。
注意: 在 v-for
中使用 v-memo
时,确保它们被用在了同一个元素上。 v-memo
在 v-for
内部是无效的。
# Vue3接收父组件传来的固定值(非动态值) attrs
props
可以接收父组件传来的 动态数据attrs
可以接收父组件传来的 固定数据 就算用动态绑定 他也是接收固定数据attrs
调用方式和emit
一样 需要setup()
参数2中结构赋值中得到该方法attrs.父组件传参的名称
来获取父组件传来的数据attrs['带-的父组件传参的名称']
如果父组件传参的名称携带-
需要这样设置
setup (props,{attrs}) {
// 普通父组件传参的名称
const ret = attrs.父组件传参的名称
// 带-的父组件传参的名称
const ret = attrs['带-的父组件传参的名称']
}
# Vue3设置Vue虚拟Dom方法 createVNode()
- 一般的组件 都只能在
template
模板中使用 在setup()
很难调用 如果希望插件能像方法一样 在setup()
中使用 就需要设置为虚拟Dom方法实现功能 并且支持props
传参 - 通过会把一些 全局弹窗 设置为虚拟Dom方法
createVNode()
具有两个参数- 参数1 表示导入的组件(需要虚拟化的组件)
- 参数2 表示传递给组件的props值(虚拟Dom的参数)
- 如果设置全局弹窗效果 需要配合
render()
函数 把createVNode()
设置的虚拟Dom 导入全局(window)的div
# 创建一个全局弹窗的Vue虚拟Dom方法
目的:在接口请求报错的时候给用户进行提示
弹窗虚拟Dom功能分析:
- 设置一个具有弹窗效果的普通组件(样式 控制参数)
- 将该组件设置为的Vue虚拟Dom方法
- 使用
createVNode()
方法把组件设置为Vue虚拟Dom方法 - 需要配合
render()
函数 把createVNode()
设置的虚拟Dom 导入全局(window)的div- 这样调用的时候 以全局window为基准弹窗 而非组件内部弹窗
- 使用
- 在Vue组件中 调用该Vue虚拟Dom方法 并且设置参数
全局弹窗的Vue虚拟Dom方法 实现步骤
设置一个具有弹窗效果的普通组件(样式 参数)
弹窗组件名是
xtx-message.vue
(模拟)(这里假设弹窗样式 就不写了)
把弹窗组件设置为Vue的虚拟Dom方法
- 使用
createVNode()
方法把组件设置为Vue的虚拟Dom方法 - 需要配合
render()
函数 把createVNode()
设置的虚拟Dom 导入全局(window)的div- 这样调用的时候 以全局window为基准弹窗 而非组件内部弹窗
- 使用
// 导入弹窗效果插件
import XtxMessage from '../../utils/xtx-message.vue'
// 导入Vue3使用的方法
import { createVNode, render } from 'vue'
// 创建一个全局组件的div(这样调用的时候 以全局window为基准弹窗 而非组件内部弹窗)
// 动态创建一个div,然后添加到页面的body里面,用于放置提示组件
const div = document.createElement('div')
div.setAttribute('class', 'xtx-message-container')
document.body.appendChild(div)
export default ({ type = 'success', text = 'error' }) => {
// 这里需要把XtxMessage组件填充到上述的div当中
// createVNode用于将组件渲染为虚拟节点
// 参数一表示导入的组件
// 参数二表示传递给组件的props属性(插件的参数)
const vnode = createVNode(XtxMessage, { type, text })
// 需要把vnode填充到div里面
// Vue2中render函数是选项,用来生成组件的模板
// Vue3中render函数是导入的
render(vnode, div)
// 3秒后把XtxMessage组件进行隐藏(其实就是把div中组件销毁)
setTimeout(() => {
// 清空div里面的内容
render(null, div)
}, 3000)
}
// -----------------------------------------
// 在setup()中使用方法
// Message({type: 'success', text: '登录失败'})
- 在Vue组件的
setup()
中使用- 先导入设置的 弹窗虚拟Dom方法
- 然后在
setup()
中使用该弹窗虚拟Dom方法
//导入封装的 弹窗虚拟Dom方法
import Message from '@/components/library/Message.js'
setup () {
// setup ()里面直接调用即可
Message({type: 'success', text: '登录失败'})
}
总结:
- 基于组件渲染虚拟节点:createVNode方法
- 渲染VNode到指定位置: render(vnode, dom)
# Vue3中创建原型链对象 (Vue2中的 this.$方法名)
在Vue3中也支持创建原型链对象 并且支持调用 通过
app.config.globalProperties
来创建 然后通过use()
实例- Vue2中使用原型链对象方法是 this.$方法名 但是在Vue3中的
setup()
中不能使用this
和$
- Vue2中使用原型链对象方法是 this.$方法名 但是在Vue3中的
注意: 创建原型链对象的方法 需要先创建虚拟Dom方法 然后才能创建原型链对象(详看31)
创建原型链对象的方法
app.config.globalProperties.$要创建的原型链方法名 = 创建的虚拟Dom方法
通常在
main.js
入口文件创建Vue3原型链对象方法命名可以不用
$
但是一般都会默认有$
创建方法
- 在
main.js
入口文件创建Vue3原型链对象方法 - 通过
app.config.globalProperties
来创建 然后通过use()
实例 - 注意: 创建原型链对象的方法 需要先创建虚拟Dom方法 然后才能创建原型链对象(详看31)
// 导入实例化的Vue3
import { createApp } from 'vue'
import App from './App.vue'
// 导入Vue3要添加的原型链方法(需要先创建虚拟Dom方法)
import Message from './components/library/Message.js'
// 创建Vue3要添加的原型链方法
const ret = {
install (app) {
// 通过config.globalProperties给其添加原型链方法
// app.config.globalProperties.$要创建的原型链方法名 = 创建的虚拟Dom方法
app.config.globalProperties.$message = Message
}
}
// 创建一个vue应用实例(.use() 可以实例化导入)
createApp(App).use(ret).mount('#app')
如果想使用原型链对象方法 需要通过
getCurrentInstance()
使用原型链对象方法 来使用 (往下看33)
# Vue3中使用原型链对象方法 getCurrentInstance()
- 在Vue3的
setup()
中 无法使用this
无法直接通过this.原型链内容
(template不受影响和Vue2一样使用即可)- 如果需要使用Vue3的
this
方法 就需要通过 Vue3方法来实现实例化方法getCurrentInstance()
- 通过结构
proxy
来代替Vue2的this
- 通过结构
- 如果需要使用Vue3的
获取原型链对象案例
<script>
// 导入getCurrentInstance 使用原型链对象方法
import { getCurrentInstance } from 'vue'
// 在setup()中使用
setup(){
// 实例化 getCurrentInstance 使用原型链对象方法 并且结构proxy
const { proxy } = getCurrentInstance()
// proxy解构后相当于Vue2的this 可以直接调用原型链的内容
const gotoCV = () => {
console.log(proxy.$t)
}
}
</script>
# Vue-router 两种跳转方法 path
和fullPath
fullPath
是路由全地址 带参数path
是路径,不带参数
跳转案例(携带参数)
如:192.168.0.1/index?page=1
fullPath
为/index?page=1path
为/index
# Vue3的 .native
修饰符
v-on
的.native
修饰符已被移除。同时,新增的emits
选项允许子组件定义真正会被触发的事件。因此,对于子组件中未被定义为组件触发的所有事件监听器,Vue 现在将把它们作为原生事件监听器添加到子组件的根元素中 (除非在子组件的选项中设置了
inheritAttrs: false
)。
2.x 语法
- 默认情况下,传递给带有
v-on
的组件的事件监听器只有通过this.$emit
才能触发。要将原生 DOM 监听器添加到子组件的根元素中,可以使用.native
修饰符:
<my-component
v-on:close="handleComponentEvent"
v-on:click.native="handleNativeClickEvent"
/>
3.x 语法
- 父组件添加点击事件
<my-component
v-on:close="handleComponentEvent"
v-on:click="handleNativeClickEvent"
/>
- 子组件接收点击事件
MyComponent.vue
<script>
export default {
emits: ['close']
}
</script>
# Vue3 升级
- 当前脚手架Vue cil4默认是安装Vue3.0版本 如果需要使用新特性需要更新Vue版本 并且需要把编译器同时更新到最新
npm i vue
npm i @vue/compiler-sfc -D