(syntax)vue3
2022-10-20 10:31:20

vue 2.0 和 vue 3.0

  • 90% 以上代码可与 vue2.0 复用
  • Composition API 作为 新增 API 不会影响旧的逻辑代码
  • Mixin 不再推荐

新特性

  • v-for 中 Ref 数组
  • v-if 与 v-for 的优先级
  • v-model
  • v-bind Merge Behavior
  • 组件
    • 异步组件
  • 自定义指令
  • 全局 API
    vue 不支持 IE8 及以下版本, 使用了 IE8 无法模拟 ECMAScript 5 特性 ,Vue 支持兼容 ECMAScript 的浏览器

vue3

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
npm init vue@latest
npm i
npm run dev
npm run build

import { createApp } from 'vue'

// 从一个单文件组件中导入根组件
import App from './App.vue'

const app = createApp(App)

createApp({
data(){
return{}
}
}).mount("#app") // 挂载应用

多个应用实例
const app1 = createApp({
/* ... */
})
app1.mount('#container-1')

const app2 = createApp({
/* ... */
})
app2.mount('#container-2')


可写计算属性
computed: {
get(){}
set(){}
}
  • v-html 双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML
  • 动态绑定多个值 objectOfAttrs: {
  • 动态参数 v-bind:[attributeName]=”url” v-on:[eventName]=”doSomething”>
  • 修饰符 @submit.prevent=”onSubmit”

事件修饰符

  • .stop
  • .prevent
  • .self
  • .capture
  • .once
  • .passive

按键别名

  • .enter
  • .tab
  • .delete (捕获“Delete”和“Backspace”两个按键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

系统按键修饰符 @keyup.alt.enter=”clear” @click.ctrl=”doSomething

  • .ctrl
  • .alt
  • .shift
  • .meta
  • .exact 修饰符允许控制触发一个事件所需的确定组合的系统按键修饰

鼠标

  • .left
  • .right
  • .middle

nextTick(() => { // 访问更新后的 DOM }) 等待一个状态改变后的 DOM 更新完成

api

  • 全局api
  • 组合式api
  • 选项式api
  • 内置内容
  • 单文件组
  • 进阶API

vue 轻量级 Vue 的核心库只关注视图层 V 一套用于构建用户界面的自底向上逐层应用的 渐进式(由浅入深)简单到复杂 JavaScript渐进式框架 架构 前 m 页面中的数据 v 视图 vm [视图层分离] 后 m 数据库 数据保存 v c 控制层

  • 声明式渲染 :声明式地描述最终输出HTML和JS状态之间的内容
  • 单文件组件 :组件化 分为 页面、组件 两种 页面上可以使用组件
  • 响应式: 编程 数据绑定响应式地更新 DOM
  • API 风格 同一个底层系统所提供的两套不同的接口。实际上,选项式 API 是在组合式 API 的基础上实现的
    • 选项式 API,我们可以用包含多个选项的对象来描述组件的逻辑 script data(){ } methods:{ } mounted()
    • 组合式 API,我们可以使用导入的 API 函数来描述组件逻辑 与<script setup> 使用
      • import { ref, onMounted } from ‘vue’ const count = ref(0) function func(){ } onMounted(() => { })

优点 简单、体积小、组件化、效率高(虚拟DOM、Diff算法)、双向数据绑定、生态丰富

  • 体积小,压缩后 33K

  • 更高的运行效率 (高效) 20kb min+ gzip 运行大小 超快虚拟 DOM

    基于虚拟 DOM,一种可以预先通过 JavaScript 进行各种计算,把最终的 DOM 操作计算出来并优化的技术,由于这个 DOM 操作属于预处理操作,并没有真实的操作,所以叫做虚拟 DOM

    虚拟DOM vs 真实DOM

  • 双向数据绑定 保持状态与视图一致

  • 生态丰富(vue-router\vuex\pinia\vue-cli\vite),学习成本低 大量的 UI 框架、常用组件

vue

  • new Vue({ el:"",data(){ return{ } } })
  • Vue.creteApp({ data(){ return{ } } }).mount("#app")
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
// vue2
var vm = new Vue({ // vm.msg == data.msg vm.$data === data vm.$el === el
el:"#app",
data(){ // '' 0 false [] null js数据类型 [1,2,3] ["","",""] [{ name:"value",version:"1.0" },{},{}]
return{
}
},
methods:{
show(){ } // or show:function(){ }
},
watch:{},
computed:{}
})

new Vue({
render: function (h) { return h(App) },
}).$mount('#app')

// vue3
const app = Vue.createApp({
data(){ }
}).mount("#app")

const ele = {
data(){}
}
Vue.createApp(ele).mount("#app")
  • Object.freeze(),这会阻止修改现有的 property,也意味着响应系统无法再追踪变化
    • var obj = { foo:bar } Object.freeze(obj)

实例property与方法 前缀 $ vm.$data vm.$el 挂载的根元素 vm.$watch('a',function(newValue,oldValue){ })

js oninput = function(){ inp.value }

指令

  • @click="func" v-on:click="event.alert" methods:{ alert(){ this.a // data.a } } 事件绑定/方法 : v-on 事件绑定 @ @[event]=”doSomething” 动态参数(vue2.6+)

  • :id="app" v-bind v-bind:id=”” v-bind:disabled=”” v-bind:href=”” v-bind:src=” “ :href=“url” :[key]=“url” 动态参数(vue2.6+) 绑定属性值 动态绑定DOM元素属性

  • {{ js表达式 }} Mustache语法 (双大括号) 的文本插值{数据绑定} 使用JavaScript 表达式

  • v-once 文本插值不可改变 标明元素或组件只能i渲染一次 执行一次性地插值,当数据改变时,插值处的内容不会更新

  • v-module v-module=“message” data(){ message:”” } 表单绑定 textarea select input

    • <input><textarea> 元素会绑定 value property 并侦听 input 事件

    • 手动连接值绑定和更改事件监听器 <input :value="text" @input="event => text = event.target.value">

    • <input type="checkbox"><input type="radio"> 会绑定 checked property 并侦听 change 事件

    • <select> 会绑定 value property 并侦听 change 事件

    • <textarea> 中是不支持插值表达式的。请使用 v-model 来替代

    • 修饰符 .lazy .number .trim

  • {{}} 、v-text、v-html

  • v-el 、v-ref vue1.0中的v-el和v-ref在2.0中被废弃了。

    • v-el 为DOM元素注册了一个索引,可以直接访问DOM元素(通过v-el我们可以获取到DOM对象) 可以通过实例的$els属性调用
    • v-ref 通过v-ref获取到整个组件(component)的对象 可以通过实例的$refs属性调用
  • v-for 循环渲染{} [] [{},{}] key ( in of )、(item,index)

    • item/index v-for="(item,index) in list"
    • in 普通数组 对象数组 对象 数字 v-for="index in 10"
    • of 替代 in 作为分隔符,接近JavaScript 迭代器的语法
    • key 提高渲染 绑定键 v-bind:key="item.id或index"
  • v-if / v-else /v-else-if 条件渲染 登陆上的tab切换

    • v-if="hidden" v-else hidden:true this.hidden =!this.hidden OR html文件上生效 template v-if="hidden" 组件渲染
    • v-if="type === 'A'" / v-else-if="type === 'B'" / v-else
  • v-show 显示隐藏

    • v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS property display

      v-show 不支持 <template> 元素,也不支持 v-else

  • v-if vs v-show

    • v-if 真正的条件渲染 操作DOM 会重新删除或创建元素 更高的切换消耗 频繁切换 确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建 惰性 懒惰的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块
    • v-show 不切换DOM 切换元素display 不管初始条件 元素总是会被渲染(基于 CSS 进行切换) 更高的初始渲染消耗 条件变化 运行条件不太可能改变
  • v-if 与 v-for 不能一起使用

    • v-for 具有比 v-if 更高的优先级。 v-if 是包括v-for的时候 v-for会提升优先级
  • v-html="" 输出html 插入HTML 需要绑定在某个元素上且能避免编译前闪现问题

  • v-text 避免未编译前的闪现问题 解决{{ text }} 中闪现的问题

  • v-cloak 在元素上添加了一个[v-cloak]的属性,直到关联的实例结束编译 css规则

  • v-pre 跳过编译这个元素和子元素,作用显示出{{双大括号}}

data(){} 数据(页面数据) \computed:{} 计算数据(一个data的值) \watch:{} 侦听(一个data的值)数据的变化 \method:{} 事件方法(要触发机制)

  • data(){} 数据

  • methods:{} 方法 表示一个具体的操作,主要书写业务逻辑

  • computed:{} 计算属性,可以理解为经过计算的data 数据的逻辑运算 计算data的属性(计算并返回给{{ }})、(计算代表逻辑),就是把模板中的复杂逻辑进行抽取出来,让模板{{ 计算属性方法 }}不要过重 可复用 是一个值 {{ msg }} //msg计算属性名 computed:{ 插值表达式的值(){ return this.data中的值 } }

    • 本质上是一个方法 不过在使用的时候是使用它们的名称 ,直接当作属性来使用 并不会把计算属性,当作方法去调用 只要计算属性,这个$function$ 内部,所用到的任何data 中的数据发送了变化,就会立即重新计算这个计算属性的
    • 属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;
    • getter(默认)/setter 属性(){ get(){ return } 返回值 set(value){ } 设置值 }
  • watch:{} 侦听器 Vue 提供了一种更通用的方法来观察和响应Vue实例上的数据变动:侦听属性,通常更好的做法是使用计算属性而不是命令式的 watch 回调。 响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的

    • watch:{ } this.$watch('question', (newQuestion) => {
    • 一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是computedmethods的结合体
  • ref

    • <input ref="input"> this.$refs.input.focus()
  • methods vs computed 区别计算属性是基于它们的响应式依赖进行缓存

    • computed 计算属性 返回一个值 使用缓存,重新调用不计算,改变时重新计算
      • 只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数
      • 缓存的作用:设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代
    • methods 方法 返回一个函数 需要触发机制 每次调用都会重新计算
  • computed vs watch data数据(){ }

    • watch 监听的data中的值变化 每时每刻监听数据或者动作的变化 在某个数据变化时做一些事情 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时
      • watch:{ data中的值(newVal,oldVal){ } //data中的值(){ data中的值 } }
    • computed 计算data中的值属性 一个数据依赖于其他数据 发送改动时运行

生命周期 vs 生命周期钩子

vue2 or vue3

  • 生命周期:从 Vue 实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期!

  • 生命周期钩子:就是生命周期事件的别名而已 生命周期钩子 = 生命周期函数 = 生命周期事件

生命周期函数分类: 创建create/mounted =>运行updated ==>销毁destory

创建(create) ==> 挂载(mounted) ==> 更新(updated) ==> 销毁(destroy)

  • ==创建期间的生命周期函数==:

    • beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性(调用报错 )组件刚刚被创建
    • created:实例已经在内存中创建,此时 data 和 methods 已经创建 OK,此时还没有开始 编译模板
    • beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面·中 挂载之前
    • mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示 挂载之后
  • ==运行期间的生命周期函数==:

    • beforeUpdate:状态更新之前 执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染 DOM 节点

    • updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!

  • ==销毁期间的生命周期函数==:

    • beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。组件销毁前调用

    • destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。组件销毁后调用

1
2
3
4
5
6
7
8
9
10
11
var vm = new Vue({
el:"#app",
init:function(){},
created: function(){}
beforeCompile: function(){}
compiled: function(){}
attached: function(){}
beforeDestroy: function(){}
destroyed: function(){}
ready: function(){}
})

组件

  • html文件 全局组件 局部组件
    • <ButtonCounter />
    • <组件名 content="Hello"></组件名> Vue.extend({ props: ["content"] template:"" }) Vue.component("别名",局部组件名); props:[“值”]子 <msg content="Hello"></msg> 父 父传子
      • 父子:子props父 标签属性值
      • props: ['msg','ddd','ccc']
      • <HelloWorld msg="center" ddd="324" ccc="234"></HelloWorld>
    • Vue.component("xxx组件名",{ data(){} template:"" }
    • <template id="tpl"> Vue.component('xxx组件名', { template: '#tpl' })
  • vue props 传递参数
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
html文件用
// vue2
div#app
<msg content="Hello"></msg> // 父组件中使用子组件msg
// 方法一 Vue.extend()
var message = Vue.extend({ // 局部组件
props: ["content"], // props 传递参数
template: "<h1>{{ content }}</h1>", // 子组件
})
Vue.component("msg",message); // 全局组件 别名

// 方法二
Vue.component("btn",{ // 直接使用没有别名
data(){ return:{ count: 'text message'} },
template: "<p>lorem {{ count }}</p>"
})

var vm = new Vue({ el:"#app" })
// 方法二 注册子组件
<div id="app">
<account></account>
</div>
// HTML中
<template id="tpl">
<div>
<h1>这是通过template元素,在定义</h1>
<h4>这是一个或</h4>
</div>
</template>
<script>
Vue.component('account', {
template: '#tpl'
});
</script>

// vue3
const app = createApp({ data(){ } })
app.component('todo-item',{
template:`<li>This is a todo</li>`
})
app.mount("#element")

vue文件
// vue
#app
template id="app"

  • 动态组件 <component> 元素和特殊的 is attribute 实现的 <component :is="currentTab"></component>
  • 注册
    • 全局注册 app.component('MyComponent', MyComponent) <ComponentA/>
    • 局部注册 components: { ComponentA: ComponentA }
  • Props 一个组件需要显式声明它所接受的 props,这样 Vue 才能知道外部传入的哪些是 props
1
2
3
4
5
6
7
export default {
props: {
title: String,
likes: Number
}
}
props: ['foo'],
  • 事件 触发与监听事件 使用 $emit 方法触发自定义事件 (例如:在 v-on 的处理函数中

  • 透传 Attribute “透传 attribute”指的是传递给一个组件,却没有被该组件声明为 propsemits 的 attribute 或者 v-on 事件监听器。最常见的例子就是 classstyleid

  • 插槽

  • 依赖注入

  • 异步组件

逻辑复用

  • 组合式函数 “组合式函数”(Composables) 是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数
  • 自定义指令 允许你注册自定义的指令 (Custom Directives)
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
const focus = {
mounted: (el) => el.focus()
}

export default {
directives: {
// 在模板中启用 v-focus
focus
}
}
<input v-focus />

const app = createApp({})

// 使 v-focus 在所有组件中都可用
app.directive('focus', {
/* ... */
})
对于自定义指令来说,一个很常见的情况是仅仅需要在 mounted 和 updated 上实现相同的行为,除此之外并不需要其他钩子。这种情况下我们可以直接用一个函数来定义指令
app.directive('color', (el, binding) => {
// 这会在 `mounted` 和 `updated` 时都调用
el.style.color = binding.value
})
<div v-demo="{ color: 'white', text: 'hello!' }"></div>
app.directive('demo', (el, binding) => {
console.log(binding.value.color) // => "white"
console.log(binding.value.text) // => "hello!"
})

  • 插件 插件 (Plugins) 是一种能为 Vue 添加全局功能的工具代码
1
2
3
4
5
6
7
8
import { createApp } from 'vue'

const app = createApp({})

app.use(myPlugin, {
/* 可选的选项 */
})

内置组件

  • Transition <Transition> 会在一个元素或组件进入和离开 DOM 时应用动画。本章节会介绍如何使用它。
  • TransitionGroup 会在一个 v-for 列表中的元素或组件被插入,移动,或移除时应用动画
  • KeepAlive 一个内置组件,它的功能是在多个组件间动态切换时缓存被移除的组件实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 非活跃的组件将会被缓存! -->
<KeepAlive :max="10"> 缓存的最大组件实例数
// include 和 exclude prop 来定制该行为
<component :is="activeComponent" />
</KeepAlive>
export default {
activated() {
// 在首次挂载、
// 以及每次从缓存中被重新插入的时候调用
},
deactivated() {
// 在从 DOM 上移除、进入缓存
// 以及组件卸载时调用
}
}
  • Teleport 是一个内置组件,它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去

应用规模化

  • 单文件组件 SFC 是一个框架指定的文件格式
  • 工具链 vite vuecli Volar(vscode 代码提示 vue 3) vetur(vue2 代码提示) vue-loader 为 webpack 提供 Vue SFC 支持的官方 loader。如果你正在使用 Vue CLI
  • 路由 veu-router
  • 状态管理 vuex pinia
  • 测试
  • 服务端渲染 SSR 是 Server-Side Rendering,即服务端渲染
    • 与客户端的单页应用 (SPA) 相比,SSR 的优势主要在于
      • 更快的首屏加载 统一的心智模型 更好的 SEO
    • Nuxt 是一个构建于 Vue 生态系统之上的全栈框架,它为编写 Vue SSR 应用提供了丝滑的开发体验
    • Vite 提供了内置的 Vue 服务端渲染支持,但它在设计上是偏底层的。如果你想要直接使用 Vite,可以看看 vite-plugin-ssr,一个帮你抽象掉许多复杂细节的社区插件。

动画

1
2
3
4
5
6
7
8
9
10
11
12
动画两组类
// mode 过渡的模式 路由动画
<transition mode="out-in">
<router-view></router-view>
</transition>
.v-enter,.v-leave-to{
opacity:0;
transform:translateX(140px);
}
.v-enter-active,.v-leave-active{
transtion:all 0.5 ease;
}

API风格

  • 选项式
  • 组合式

声明式渲染

SFC 单文件组件

  • template script style 组成
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// vue2 选项式
<p ref="p">hello</p> // this.$refs.p 挂载之后就能使用
<ChildComp />

export default{
data(){
return{
msg:'',
text:''
titleClass:'title', // title 类名
todoData: null
}
},
methods:{ // 方法对象 //
方法名(){

},
onInput(e){
this.text = e.target.value // 获取输入框的值
}
},
computed:{
filteredTodos() { return }
}
import ChildComp from './ChildComp.vue'
components: {
ChildComp
}
mounted(){
this.$refs.p.textContent = 'mounted!' // textContent修改
// 网络请求
async fetchData() {
this.todoData = null
const res = await fetch(
`https://jsonplaceholder.typicode.com/todos/${this.todoId}`
)
this.todoData = await res.json()
}
}
}
<input :value="text" @input="onInput" v-model="text">

props
// 在子组件中
export default {
props: {
msg: String
}
}
// 父属性子props:{}
<ChildComp :msg="greeting" /> //

子组件还可以向父组件触发事件
export default {
// 声明触发的事件
emits: ['response'],
created() {
// 带参数触发 this.$emit() 的第一个参数是事件的名称。其他所有参数都将传递给事件监听器。
this.$emit('response', 'hello from child')
}
}

<ChildComp @response="(msg) => childMsg = msg" />
// 父传子
除了通过 props 传递数据外,父组件还可以通过插槽 (slots) 将模板片段传递给子组件

<ChildComp>
This is some slot content!
</ChildComp>

子 slot在子组件上
<!-- 在子组件的模板中 -->
<slot/>
<slot>Fallback content</slot>



// vue3 组合式
// ref() 可以接受任何值类型。ref 会返回一个包裹对象,并在 .value 属性下暴露内部值
// reactive() 只适用于对象 (包括数组和内置类型,如 Map 和 Set) 声明响应式状态行为与普通对象一样
<p ref="p">hello</p>
<ChildComp />

<script setup>
import { ref,reactive,computed,watch,onMounted } from 'vue'

// vue3组件不用注册 直接导入不同注册
import ChildComp from './ChildComp.vue'
// data
const count = ref('1000') // ref(['']) ref({ count:'' })
count.value
const reactive = ({ count: '100'})
const titleClass = ref('title') // title 类名 const 变量 = 值
// methods
function 方法名(){}
function onInput(e){ this.text = e.target.value }

computed(() => {})
const p = ref(null)
//生命周期 onUpdated 和 onUnmounted
onMounted(()=>{
p.value.textContent = 'Message'
})

const count = ref(0)

watch(count, (newCount) => {
// 没错,console.log() 是一个副作用
console.log(`new count is: ${newCount}`)
})

// 网络请求
const todoData = ref(null)

async function fetchData() {
const res = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
todoData.value = await res.json()
}
fetchData()

// 父子通信
子 // vue2 props:{}
const props = defineProps({ // defineProps() 是一个编译时宏,并不需要导入 props固定
msg: String // msg 参数
})


import { ref } from 'vue'
import ChildComp from './ChildComp.vue'
const greeting = ref('Hello from parent')

<ChildComp :msg="greeting" /> // :子="父"


子组件还可以向父组件触发事件 子传父 子emit=defineEmits([]) emit(事件,参数)
父 ChildComp @事件="(msg) => 父 = 子"
// 子
// 声明触发的事件
const emit = defineEmits(['response'])
// 带参数触发
emit('response', 'hello from child') emit(事件,参数)

// 父
const childMsg = ref('No child msg yet')
<ChildComp @response="(msg) => childMsg = msg" /> @事件="(参数) => 父参数 = 参数"
<p>{{ childMsg }}</p>
// 插槽和vue2一样 把父的内容放在子上 插槽slot在子上

<script>

HTML 文件

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
HTML 文件
#app
选项式
#app
<child-comp></child-comp>
<p ref="p">hello</p>
<child-comp :msg="greeting"></child-comp>
<child-comp @response="(msg) => childMsg = msg"></child-comp>
<script type="module">

import { createApp } from 'vue'
import ChildComp from './ChildComp.js'

const app = createApp({ // new Vue(el:"#app",) => createApp
data(){
return{
count:0
titleClass:'title' // titleClass 名 title 值
greeting: 'Hello from parent'
childMsg: 'No child msg yet'
}
},
methods:{
increment(){
this.count++ // 使用 this 来访问组件示例。组件实例会暴露 data 中声明的数据属性。我们可以通过改变这些属性的值来更新组件状态
}
},
computed:{
filteredTodos() { return 1000 }
},
mounted(){
this.$refs.p.textContent = 'mounted!'
},
components: {
ChildComp // 导入组件 注册组件 使用组件
}
}).mount("#app")


多个应用实例
const app1 = createApp({
/* ... */
})
app1.mount('#container-1')

const app2 = createApp({
/* ... */
})
app2.mount('#container-2')

</script>


// ./ChildComp.js
export default {
template: `
<h2>A Child Component!</h2>
`
}

export default {
props: {
msg: String
},
template: `
<h2>{{ msg || 'No props passed yet' }}</h2>
`
}

export default {
emits: ['response'],
created() {
this.$emit('response', 'hello from child')
},
template: `
<h2>Child component</h2>
`
}

插槽
export default {
template: `
<slot>Fallback content</slot>
`
}


组合式 // setup(){ return{} }
<script type="module">
import { createApp, reactive, ref } from 'vue'
import ChildComp from './ChildComp.js'

createApp({
components: {
ChildComp
}
setup() { // 传入 createApp() 的对象是一个 Vue 组件。组件的状态应该在 setup() 函数中声明,并使用一个对象返回
const counter = reactive({ count: 0 })
const message = ref('Hello World!')

function increment() {
count.value++
}

function onInput(e) {
text.value = e.target.value
}

const filteredTodos = computed(() => {
return hideCompleted.value
? todos.value.filter((t) => !t.done)
: todos.value
})

onMounted(() => {
p.value.textContent = 'mounted!'
})

function fetchData() { }


fetchData()
return {
counter,
message,
increment,
onInput,
filteredTodos
}
}
}).mount('#app')

export default {
template: `
<h2>A Child Component!</h2>
`
}
</script>
  • :id="" v-bind:id="" 属性绑定
  • @click="" v-on=""
  • v-module
  • v-if="" v-else
  • v-for
  • v-ref 操作DOM $refs.元素.textContent
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
<script>
// 给每个 todo 对象一个唯一的 id
let id = 0
export default {
data() {
return {
newTodo: '',
hideCompleted:false,
todos: [
{ id: id++, text: 'Learn HTML',done:true },
{ id: id++, text: 'Learn JavaScript',done:true },
{ id: id++, text: 'Learn Vue',done:false }
]
}
},
computed: { // 计算属性
filteredTodos() {
return this.hideCompleted
? this.todos.filter((t) => !t.done)
: this.todos
}
},
methods: {
addTodo() {
this.todos.push({ id: id++, text: this.newTodo,done:false })
this.newTodo = ''
},
removeTodo(todo) {
this.todos = this.todos.filter((t) => t !== todo)
}
},
}
</script>

<script setup>
import { ref } from 'vue'

let id = 0

const newTodo = ref('')
const hideCompleted = ref(false)
const todos = ref([
{ id: id++, text: 'Learn HTML',done:true },
{ id: id++, text: 'Learn JavaScript',done:true },
{ id: id++, text: 'Learn Vue',done:false }
])

const filteredTodos = computed(() => {
return hideCompleted.value
? todos.value.filter((t) => !t.done)
: todos.value
})

function addTodo() {
todos.value.push({ id: id++, text: newTodo.value,done:false })
newTodo.value = ''
}

function removeTodo(todo) {
todos.value = todos.value.filter((t) => t !== todo)
}
</script>

<template>
<form @submit.prevent="addTodo">
<input v-model="newTodo">
<button>Add Todo</button>
</form>
<ul>
<li v-for="todo in filteredTodos" :key="todo.id">
<input type="checkbox" v-model="todo.done">
<span :class="{ done: todo.done }">{{ todo.text }}</span>
<button @click="removeTodo(todo)">X</button>
</li>
</ul>
<button @click="hideCompleted = !hideCompleted">
{{ hideCompleted ? 'Show all' : 'Hide completed' }}
</button>
</template>


<script setup>
import { ref, watch } from 'vue'

const todoId = ref(1)
const todoData = ref(null)

async function fetchData() {
todoData.value = null
const res = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
todoData.value = await res.json()
}

fetchData()

watch(todoId, fetchData)
</script>

<template>
<p>Todo id: {{ todoId }}</p>
<button @click="todoId++">Fetch next todo</button>
<p v-if="!todoData">Loading...</p>
<pre v-else>{{ todoData }}</pre>
</template>

vue3.2

  • 语法 setup defineProps defineEmit defineExpose v-memo style v-bind

setup

  • 语法糖

  • setup(){} return{ x,y,z }

  • 简化vue2配置项的写法 组件只需引入不用注册,属性和方法也不用返回 也不用写setup函数,也不用写export default , 甚至是自定义指令也可以在我们的template中自动获得 不再需要进行return

  • 使用

    • <script setup> 声明的顶层的绑定 (包括声明的变量,函数声明,以及 import 引入的内容) 都可以在模板中直接使用
      • 普通的 <script> 只在组件被首次引入的时候仅执行一次不同
      • <script setup>中的代码会在每次组件实例被创建的时候执行 初始化的赋值等在组件每次实例创建时都重新执行一次
  • 特点

    • 自动注册 无需要像vue2componets:{ xxxx }注册 只要导入
  • 注意

    • 不需要 this.XX的形式去使用
    • return 返回他的值
    • setup的两个参数 一个是props 一个是content 全局上下文
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
//vue3
// 方法一
<script setup> // 里面的代码编译成组件setup
import { ref } from 'vue'
let str = ref('String')
</script>

// 配置项 方法二
setup(){
// data
let num = 1000;
let str = 'String';
const arr = [1,2,3,4,5]
function fn(){ console.log(setup) }
}
return{
num,str,arr,fn
}

//vue2
// 数据源
data(){ return{} }
methods:{}
created:{}
computed:{}
...

组件通信

  • defineProps Props传参 父 => 子
  • defineEmit 事件传参 子 => 父
  • defineExpose 组件暴露出自己的属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// defineProps 
// 父
<son :title="msg">

import { ref } from 'vue'
import son from './son.vue'
const msg = ref('Message')

// 子
h1{{ props.title }}
import { defineProps } from 'vue'
const props = defineProps({
title:{
type:String // props.title
},
info:{
type:String,
default:'----'
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// defineEmit
// 父
<son @getChild="getChild"></son>

import { ref } from 'vue'
import son from './Son.vue'
let data = ref(null)
const getChild = (e) =>{
data.value = e // 子组件的值
}

// 子
<button @click="toEmits">点击 子传父</button>

import { defineEmits,ref } from 'vue'
// 定义
const emit = defineEmits(['getChild'])
const toEmits = () =>{
emit('getChild','要传递的参数')
}
let hander1Click=():void=>{
$myemit('myAdd','新增的数据')
}
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
// defineExpose
// 子
import {ref, defineExpose, reactive} from 'vue'

let obj = reactive({
name: 'zs',
age: 18
})
let msg = ref('Message');

defineExpose({
obj,
msg
})
// 父
<button @click="parExpose"> // 方法
<son ref="par" /> // 元素

import son from './Son.vue'
import {ref,defineEmits,defineProps} from 'vue'

cons par = ref()
const parExpose = () =>{
// par.value.obj
// par.value.msg
}

新增指令 v-memo 记住一个模板的子树,元素和组件上都可以使用

  • 缓存模板中的一部分数据。 只创建一次,以后就不会再更新了。也就是说用内存换取时间

style v-bind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<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>

hook函数

  • vue3 hook 相当于 vue2 的 mixin, 不同在与 hooks 是函数
  • Vue3 的 hook函数 可以帮助我们提高代码的复用性, 让我们能在不同的组件中都利用 hooks 函数
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
hooks/use.js
import {reactive, onMounted, onBeforeUnmount} from 'vue'
export default function () {
//展示的数据 可以通过App.vue 界面去隐藏
let point = reactive({
x: 0,
y: 0
})
//获取鼠标点击事件
function savePonint(event) {
point.x = event.pageX
point.y = event.pageY
console.log(event.pageX, event.pageY)
}
//现实之后调用 挂载完毕
onMounted(() => {
window.addEventListener('click', savePonint)
})
//在隐藏之前调用 卸载之前
onBeforeUnmount(() => {
window.removeEventListener('click', savePonint)
})
return point
}

<template>
<h2>当前求和:{{ sum }}</h2>
<button @click="sum++">点我加一</button>
<hr>
<h2>当前鼠标点击坐标为:x:{{ point.x }},y:{{ point.y }}</h2>
</template>

<script>
import {ref} from 'vue'
//复用的usePoint
import usePoint from "../hooks/usePoint";

export default {
name: 'App',
setup() {
//数据
let sum = ref(0)
let point = usePoint()
return {sum,point}
},
}
</script>

toRefs

vue库

  • Vue 没有内置支持防抖和节流,但可以使用 Lodash 等库来实现
上一页
2022-10-20 10:31:20
下一页