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
修饰符允许控制触发一个事件所需的确定组合的系统按键修饰
鼠标
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算法)、双向数据绑定、生态丰富
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
| var vm = new Vue({ el:"#app", data(){ return{ } }, methods:{ show(){ } }, watch:{}, computed:{} })
new Vue({ render: function (h) { return h(App) }, }).$mount('#app')
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-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) => {
- 一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是
computed
和methods
的结合体
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
生命周期函数分类: 创建create/mounted =>运行updated ==>销毁destory
创建(create) ==> 挂载(mounted) ==> 更新(updated) ==> 销毁(destroy)
==创建期间的生命周期函数==:
- beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性(调用报错 )
组件刚刚被创建
- created:实例已经在内存中创建,此时 data 和 methods 已经创建 OK,此时还没有开始 编译模板
- beforeMount:此时已经完成了模板的编译,但是还没有
挂载到页面·中 挂载之前
- mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示
挂载之后
==运行期间的生命周期函数==:
==销毁期间的生命周期函数==:
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'],
|
逻辑复用
- 组合式函数 “组合式函数”(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: { focus } } <input v-focus /> const app = createApp({})
app.directive('focus', { }) 对于自定义指令来说,一个很常见的情况是仅仅需要在 mounted 和 updated 上实现相同的行为,除此之外并不需要其他钩子。这种情况下我们可以直接用一个函数来定义指令 app.directive('color', (el, binding) => { el.style.color = binding.value }) <div v-demo="{ color: 'white', text: 'hello!' }"></div> app.directive('demo', (el, binding) => { console.log(binding.value.color) console.log(binding.value.text) })
|
- 插件 插件 (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() { } }
|
- 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 的优势主要在于
- Nuxt 是一个构建于 Vue 生态系统之上的全栈框架,它为编写 Vue SSR 应用提供了丝滑的开发体验
- Vite 提供了内置的 Vue 服务端渲染支持,但它在设计上是偏底层的。如果你想要直接使用 Vite,可以看看 vite-plugin-ssr,一个帮你抽象掉许多复杂细节的社区插件。
动画
1 2 3 4 5 6 7 8 9 10 11 12
| 动画两组类
<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 单文件组件
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
| <p ref="p">hello</p> // this.$refs.p 挂载之后就能使用 <ChildComp />
export default{ data(){ return{ msg:'', text:'' titleClass:'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!' 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 } }
<ChildComp :msg="greeting" />
子组件还可以向父组件触发事件 export default { emits: ['response'], created() { 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>
<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({ data(){ return{ count:0 titleClass:'title' greeting: 'Hello from parent' childMsg: 'No child msg yet' } }, methods:{ increment(){ this.count++ } }, 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>
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> ` }
组合式 <script type="module"> import { createApp, reactive, ref } from 'vue' import ChildComp from './ChildComp.js'
createApp({ components: { ChildComp } 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>
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>
中的代码会在每次组件实例被创建的时候执行 初始化的赋值等在组件每次实例创建时都重新执行一次
特点
- 自动注册 无需要像vue2
componets:{ 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
|
<script setup> import { ref } from 'vue' let str = ref('String') </script>
setup(){ let num = 1000; let str = 'String'; const arr = [1,2,3,4,5] function fn(){ console.log(setup) } } return{ num,str,arr,fn }
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
|
<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 }, 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
|
<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
|
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 = () =>{ }
|
新增指令 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 { 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 () { 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'
import usePoint from "../hooks/usePoint";
export default { name: 'App', setup() { let sum = ref(0) let point = usePoint() return {sum,point} }, } </script>
|
toRefs
vue库