Vue.createApp({
template: `..`,
data() {
return { count: 100 }
},
methods: {..}
})
1.两个概念:计数器案例原生实现、vue实现
编程范式:命令式、声明式
架构模式:MVC、MVVM
2.模板语法tempate-Mustache
template元素是一种在客户端保存内容的机制,在加载时不被渲染,可以被js实例化(显示)
<template>
<div>{{ count }}</div> A,基本用法
<div>{{ count * 2 }}</div> B,表达式
<div>{{ String(count).split("").reverse().join("")}}</div>
<div>{{ getReserveCount() }}</div> C,方法-计算属性=computed
<div>{{ isShow = count === 0 ? false : true }}</div> D,三元运算
</template>
3.vue3.0 data只能是函数:
data返回的对象会被vue响应式系统劫持proxy,之后对该对象的修改和访问都会在代理中处理
vue3中template下可以有多个根元素
v-bind="obj" 对象属性作为元素属性
v-on="{click: handleClick, mousemove: mouseMove}" 绑定多个事件
@click="handleClick($event, 'tom')"
v-show: 不能用在template标签上,<template v-show="true"> 也不会被渲染 但是已被处理
v-for="(value, key, index) in obj"
v-for="(item, index) in arr"
v-for="(num, index) in 10" //从1开始打印到10
4.diff算法
template -> VNode -> Virtual Dom -> 真实dom
differ算法:新旧VNode对比,patch更新
没key的过程:patchUnKeyedChildren
尽可能的复用、修改
[a,b,c] [a,c,d]对比,b的位置更新未c, c的位置更新未d,
如果新VNode数组比旧长,就创建新节点
短,就删除旧数组
性能低
有key的过程:patchKeyedChildren
会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素
1.从头遍历
2.从尾巴遍历
3.旧节点遍历完了还有节点,就新增节点
4.新节点遍历完了还有旧节点,就移除
源码:packages\runtime-core\src\renderer.ts
5.computed:getter setter
<button @click="changeFullName">change</button>
<h2>{{fullName}}</h2>
data() {
return {firstName: 'ting', lastName: 'liu'}
}
computed: {
fullName: {
get() {
return this.firstName + ' ' + this.lastName
},
set(newValue) {
const name = this.fullName.split(' ').reverse().join(' ')
this.firstName = name[0]
this.lastName = name[1]
},
}
}
methods: {
changeFullName() {
this.fullName = 'ting1 liu1' // 事件改变计算属性的值
},
}
源码:packages\runtime-core\src\componentOptions.ts (methods/computed)
6.watch
<input type="text" v-model="question" />
<div @click="changeInfo">changeInfo</div>
data() {
return { question: '', info: {name: 'lt'} }
// 监听对象: info: {name: 'lt'} watch deep
// 监听数组:friend: [{name: 'tom1'}, {name: 'tom2'}] 监听item.name的变化,常用在组件中,通过prop属性,再使用watch深度监听item.name
}
watch: {
question(newVal, oldVal) { // A
// 逻辑处理
},
info: {
handler(newVal, oldVal) { // 这步 B === A
// 逻辑处理
},
deep: true,
immediate: true
},
'info.name'(newVal, oldVal) { // 只监听info的name属性变化 C
// 逻辑处理
}
},
created() {
const unwatch =
this.$watch('info', function(newVal, oldVal) {...}, {deep: true, immediate: true}) // D
unwatch() // 表示取消监听
},
// watch function|obj|string BD相同,B先执行
methods: {
changeInfo() {
this.info = {name: 'tom', age: 13}
},
}
7.知识补充:
1.深浅拷贝: 取决于复制的是实例还是内存地址
参考文章:【 js 基础 】 深浅拷贝 - 知乎
总结:Array 的 slice 和 concat 方法 和 jQuery 中的 extend 复制方法
他们都会复制第一层的值,对于 第一层 的值都是 深拷贝,而到 第二层 的时候 Array 的 slice 和 concat 方法就是 复制引用 ,jQuery 中的 extend 复制方法 则 取决于 你的 第一个参数, 也就是是否进行递归复制。所谓第一层 就是 key 所对应的 value 值是基本数据类型,也就像上面栗子中的name、age,而对于 value 值是引用类型 则为第二层
slice和contact对于第一层是深拷贝,但对于多层的时候,是复制的引用,所以是浅拷贝
实现深拷贝的方法:
1.递归
2.$.extend()
3.JSON.parse()/JSON.stringify()
8.this
1.this出现的时机:通过字面量创建对象,没有this就是这样: 详见-This理解
var obj = {
name: "why",
running: function() {
console.log(obj.name + " running");
},
eating: function() {
console.log(obj.name + " eating");
},
studying: function() {
console.log(obj.name + " studying");
}
}
9.v-model修饰符
.lazy => @input -> @change
.number
.trim
<input type="text" v-model.lazy="inputVal" />
v-model自定义组件使用,
组件名: 短横线,推荐 component-a
驼峰 ComponentA
10.小知识:
1.父->子传值 props
父 :obj="message"
11.子 props: ['a', 'b'] props数组方式解构
template中直接用a, b
缺点:不能做类型检查
22.子 props:{}
'a': {type: Object, default() {return {...}} props引用类型 default返回函数
2.<template>中属性名使用-连接,【大小写不区分,转为小写】
3.非props属性使用:
<template> <div :class="$attrs.class">
inheriteAttrs: false
props: {...}
4.this.$emit('eventName') // 需要在当前页面emits: ['eventName'],
1.非父子组件通信-子孙:Provide inject
2.事件传递: 事件总线 mitt
11.插槽v-slot
1.slot组件页面
<div class="flex">
<div class="left"><slot name="left"></slot></div>
<div class="center"><slot name="center"></slot></div>
<div class="right"><slot :name="name"></slot></div>
</div>
props:{name: {type: String, default: ''}}
2.引用slot组件页面
<slot-a>
<template v-slot="left"><a>什么什么什么什么</a></template>
<template #[name]><a>动次打次动次打次</a></template>
</slot-a>
data() {return {name: 'tom'}}
v-slot:[name] 简写为 #[name]
使用格式
slot标签定义:
正常布局
<div><slot name="xxx"><slot></div>
<div><slot :name="name"><slot></div> props: xxx
<template v-for="..." :key=".."><slot :item="item" :index="index"></slot> </template> 作用域插槽
使用:
<div><template v-slot="xxx">Content<template></div>
<div :xxx="xxx"><template #[xxx]>Content<template></div> 简写
<slotCpn><template v-slot="scope">Content内使用</template>
</slotCpn> 作用域插槽
vue中编译作用域:
父级模板中的所有内容都是在父级作用域中编译的
子级模板中的内容都是在子级作用域中编译的
作用域插槽: 父取子数据
12.动态组件:
1.<component :is="cpn" :name="name" age="10"></component>
cpn: 需满足Vue.components注册或组件内的components注册
2.缓存组件状态:keep-alive
<keep-alive include="home,about"><component></component>
前提:组件页面的name一定要对应上
第一次进page,mounted activated都执行,
mounted仅执行第一次初始化,后activited
activited次次执行
13.异步加载组件:
1.defineAsyncComponent + suspense
b.<suspense>
<template #default><cpn></cpn></template>
<template #fallback><loading></loading></template>
</suspense>
a.const cpn = defineAsyncComponent(()=> import(''''))
components: {cpn}
14.自定义input组件使用v-modal:
<inputBlock v-modal:name="name" v-modal:title="title"></iinputBlock>
// 组件中:
<div><input v-modal="name" /><input v-modal="title"></div>
props: {name:..., title: ....}
13.vue中不能加空格的情况:
1.vue2 组件自定义双向绑定:[update:xxx]
2.vue3 keep-alive include="home,about"
14.vue中提高首屏加载方法:
1.webpack分包打包
import('../..').then(res=> {res.sum(10,20)})
vue中:defineAsyncComponent 见13
写法一:
import { defineAsyncComponent } from 'vue'
const aaa = defineAsyncComponent(() => import('.../....'))
写法二: loadingComponent占位组件const aaa = defineAsyncComponent({
loader: import('./pages/asyncCpn.vue'),
loadingComponent: loading,
errorComponent: xxxx // 错误加载这个组件
delay: 2000, // 2s后再去加载loadingComponent})
15.动画:
1.基本用法:
<button @click="isShow = !isShow">切换</button>
<transition name="tom" mode="in-out" appear>
<h2 v-if="isShow" style="display: inline-block">哈哈哈哈</h2>
</transition>
.tom-enter-from,
.tom-leave-to {
opacity: 0;
}
.tom-enter-to,
.tom-leave-from {
opacity: 1;
}
.tom-enter-active,
.tom-leave-active {
transition: opacity 2s ease;
}
2.帧动画:
<button @click="isShow = !isShow">切换</button>
<transition name="tom">
<h2 v-if="isShow" style="display: inline-block">哈哈哈哈</h2>
</transition>
.tom-enter-active,
.tom-leave-active {
animation: bounce 2s ease;
}
@keyframes bounce {
0% {
transform: scale(0)
}
50% {
transform: scale(1.5)
}
100% {
transform: scale(1)
}
}
3.animate.css
<button @click="isShow = !isShow">切换</button>
<transition name="tom" enter-active-class="animate__animated animate_fadeInDown"
leave-active-class="animate__animated animate_flipInY>
<h2 v-if="isShow" style="display: inline-block">哈哈哈哈</h2>
</transition>
.animate_flipInY {
transition-direction: reverse;
}