一、开始搭建一个vue3项目
- 安装vue
yarn global add @vue/cli
- 通过在终端中运行以下命令,可以使用 Vite 快速构建 Vue 项目。
$ yarn create vite-app <project-name>
$ cd <project-name>
$ yarn
$ yarn dev
- 为什么是vite而不是cli?链接
1、vue-cli基于webpack,是vue2的前端构建工具,在启动一些大型项目的时候启动较慢,vite的尤雨溪团队在发布vue3的前端构建工具,优化了启动慢的问题。
2、两者在生产环境下都是基于源代码文件打包,但是在开发环境下,vite是基于原生es6,无需对代码进行打包,浏览器可以直接调用,在开发环境中体验及其愉快。
二、vue3
指令集合
1、v-bind:用于数据绑定,等同于使用{{}}
<span v-bind:title="message">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>
//缩写
<span :title="message">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>
<script>
export default {
name: 'HelloWorld',
data() {
return {
message: 'You loaded this page on ' + new Date().toLocaleString()
}
}
}
</script>
2、v-on:添加一个事件监听器
<button v-on:click="reverseMessage">反转 Message</button>
//缩写
<button @click="reverseMessage">反转 Message</button>
methods: {
reverseMessage() {
this.message = this.message
.split('')
.reverse()
.join('')
}
}
- 可以同时监听多个方法,如下
<button @click="one($event), two($event)">
Submit
</button>
- 事件修饰符
Vue.js 为 v-on 提供了事件修饰符
.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():
<!-- 阻止单击事件继续传播 -->
<a @click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form @submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a @click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form @submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div @click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div @click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 -->
<a @click.once="doThis"></a>
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div @scroll.passive="onScroll">...</div>
- 键盘修饰符
Vue 允许为 v-on 或者 @ 在监听键盘事件时添加按键修饰符
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input @keyup.enter="submit" />
<!-- 处理函数只会在 $event.key 等于 'PageDown' 时被调用 -->
<input @keyup.page-down="onPageDown" />
常用的如下:.enter、.tab、.delete (捕获“删除”和“退格”键)、.esc、.space、.up、.down、.left、.right
- 系统修饰键
仅在按下相应按键时才触发鼠标或键盘事件的监听器
.ctrl、.alt、.shift、.meta
<!-- Alt + Enter -->
<input @keyup.alt.enter="clear" />
- .exact 修饰符
允许你控制由精确的系统修饰符组合触发的事件
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>
- 鼠标按钮修饰符
.left、.right和.middle
3、v-model:实现表单输入和应用状态之间的双向绑定
可以在表单 input、textarea 及 select 元素上创建双向数据绑定
<p>message:{{ message }}</p>
<input v-model="message" />
<!--单行输入文本-->
<input v-model="message" placeholder="edit me" />
<!--多行输入文本-->
<textarea v-model="message" placeholder="add multiple lines"></textarea>
<!--复选框-->
<input type="checkbox" id="checkbox" value="jack" v-model="checked" />
checked为字符串的时候,选中或者未选中的值为true和false,checked为[]时,值为value的值
<!--单选框-->
<input type="radio" id="one" value="One" v-model="picked" />
<!--选择框-->
<select v-model="selected">
<option disabled value="">Please select one</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
修饰符:
- .lazy
v-model默认绑定值msg的数据在输入的时候同步改变,加上lazy之后默认change事件之后再同步改变
<input v-model.lazy="msg" />
- .number
自动将输入值转换为数值类型
<input v-model.number="age" type="number" />
- .trim
自动过滤用户输入的首尾空白字符
<input v-model.trim="msg" />
4、v-if:条件表达式
<span v-if="seen">现在你看到我了</span>
<h1 v-else>Oh no 😢</h1>
v-else-if,顾名思义,充当 v-if 的“else-if 块”,可以连续使用:
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
v-else-if,v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别。
v-show
<h1 v-show="ok">Hello!</h1>
v-show和v-if的区别:v-show元素初始总是会被渲染,之后基于css进行样式切换,v-if条件为真的时候才会被渲染。v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
5、v-for:数据的循环表达
建议尽可能在使用 v-for 时提供 key attribute
<li v-for="item in items" :key="item.id">
{{ item.text }}
</li>
状态维护策略:当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
items可以是数组,也可以是对象
- items是数组时:
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
- items是对象时:
<li v-for="(value, key, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
value-键值 key-键名 index-索引
也可以用of代替in
<div v-for="item of items"></div>
6、v-once:一次性绑定,数据改变时,插值内容不再改变
<span v-once>这个将不会改变: {{ msg }}</span>
7、v-html:解释html代码
<p>Using mustaches: {{ rawHtml }}</p>
<span v-html="rawHtml"></span>
注意:动态渲染html是非常危险的,容易导致XSS攻击,不要将用户输入的内容作为插值
8、动态参数绑定
<a v-on:[eventName]="doSomething"> ... </a>
在这个示例中,当 eventName 的值为 “focus” 时,v-on:[eventName] 将等价于 v-on:focus
#### 组件化
```javascript
//创建一个组件
const app = Vue.createApp({
name: 'HelloWorld',
props: {
msg: String
},
data() {
return { count: 4 }
},
methods: {
reverseMessage() {
}
}
})
//挂载
const vm = app.mount('#app')
还有各种其他的组件选项,可以将用户定义的 property 添加到组件实例中,例如 methods,props,computed,inject 和 setup.
生命周期
1、生命周期钩子
钩子函数:
beforeCreate、
created、
beforeMounte、
mounted、
beforeUpdate、
updated 和
beforeUnmounte、
unmounted
2、生命周期图示
data和methods
1、data
data是一个函数,返回一个对象,以$data形式存储在组件实例中。
const app = Vue.createApp({
name: 'HelloWorld',
data() {
return { count: 4 }
}
})
const vm = app.mount('#app')
vm.count = 4
vm.$data.count = 4
2、methods
const app = Vue.createApp({
name: 'HelloWorld',
methods: {
reverseMessage() {
}
}
})
const vm = app.mount('#app')
vm.reverseMessage() //执行
vue自动为methods方法绑定this,指向组件实例,在定义 methods 时应避免使用箭头函数,因为这会阻止 Vue 绑定恰当的 this 指向。
3、防抖和节流
Vue 没有内置支持防抖和节流,但可以使用 Lodash等库来实现。如下:
若组件仅仅使用一次:
<script src="https://unpkg.com/lodash@4.17.20/lodash.min.js"></script>
<script>
Vue.createApp({
methods: {
// 用 Lodash 的防抖函数
click: _.debounce(function() {
// ... 响应点击 ...
}, 500)
}
}).mount('#app')
</script>
但若组件复用,则多个组件实例会共享同一个防抖函数,为了各个组件实例相互独立,可以在created 里添加该防抖函数。
app.component('save-button', {
created() {
// 用 Lodash 的防抖函数
this.debouncedClick = _.debounce(this.click, 500)
},
unmounted() {
// 移除组件时,取消定时器
this.debouncedClick.cancel()
},
methods: {
click() {
// ... 响应点击 ...
}
},
template: `
<button @click="debouncedClick">
Save
</button>
`
})
计算属性和侦听器
计算属性vs方法vs侦听器
<div id="demo">{{ fullName }}</div>
侦听器
const vm = Vue.createApp({
data() {
return {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
}
},
watch: {
firstName(val) {
this.fullName = val + ' ' + this.lastName
},
lastName(val) {
this.fullName = this.firstName + ' ' + val
}
}
}).mount('#demo')
计算属性
计算属性默认只有 getter,不过在需要时你也可以提供一个 setter
const vm = Vue.createApp({
data() {
return {
firstName: 'Foo',
lastName: 'Bar'
}
},
computed: {
fullName:{
// getter
get() {
return this.firstName + ' ' + this.lastName
},
// setter
set(newValue) {
const names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
}).mount('#demo')
方法
<div id="demo">{{ fullName() }}</div>
const vm = Vue.createApp({
data() {
return {
firstName: 'Foo',
lastName: 'Bar'
}
},
methods: {
fullName() {
return this.firstName + ' ' + this.lastName
}
}
}).mount('#demo')
计算属性和方法的不同:计算属性是基于它们的反应依赖关系缓存的,相比之下,每当触发重新渲染时,调用方法将总会再次执行函数(我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 list,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 list。如果没有缓存,我们将不可避免的多次执行 list 的 getter!如果你不希望有缓存,请用 method 来替代)
class和style绑定
1、class
对象语法
使用v-bind动态切换class
<div :class="{ active: isActive, 'text-danger': hasError }"></div>
data() {
return {
isActive: true,
hasError: false
}
}
渲染的结果为
<div class="active"></div>
数组语法
<div :class="[activeClass, errorClass]"></div>
data() {
return {
activeClass: 'active',
errorClass: 'text-danger'
}
}
三元表达式
<div :class="[isActive ? activeClass : '', errorClass]"></div>
当组件存在多个根元素,可以定义哪些部分接受这个类,可以使用 $attrs 组件属性执行此操作,如下
<div id="app">
<my-component class="baz"></my-component>
</div>
const app = Vue.createApp({})
app.component('my-component', {
template: `
<p :class="$attrs.class">Hi!</p>
<span>This is a child component</span>
`
})
2、style
对象语法
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
绑定一个对象可以使得语法更清晰
<div :style="styleObject"></div>
data() {
return {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
}
数组语法
数组中包含多个对象
<div :style="[baseStyles, overridingStyles]"></div>
vue方法
数组方法:
Vue 将被侦听的数组的变更方法进行了包裹
如下方法会改变原始数组
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
如下方法不改变原始数组,返回一个新的数组 - filter()、concat() 和 slice()
vue组件基础
- 单个组件
// 创建一个Vue 应用
const app = Vue.createApp({})
// 定义一个名为 button-counter 的新全局组件
app.component('button-counter', {
data() {
return {
count: 0
}
},
template: `
<button @click="count++">
You clicked me {{ count }} times.
</button>`
})
单个组件可以进行任意次数的复用<button-counter></button-counter>
- 组件注册:
组件的注册分为两种类型:全局注册和局部注册 - 父子组件通信
父子组件之间的通信为单向数据流,只能从父组件流向子组件
1、 一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。
注意:1、尽量避免再子组件内部改变props内部的值,如有必要,将props的值作为data内部定义数据的初始值或者以props的值来定义一个计算属性;2、通过props传递数组和对象的时候是通过引用传入的,擅自改动在子组件中的状态会影响到父组件中的数值
app.component('button-counter', {
props: ['title'],
})
<button-counter title='相信自己'></button-counter>
props也可以指定数据类型,如下:
props: {
title: String
}
title可以时任意数据类型,字符串,数组和对象等等,仅需要写在双引号内。对于对象的传递,有如下方法
post: {
id: 1,
title: 'My Journey with Vue'
}
<button-counter v-bind:id="post.id" v-bind:title="post.title"></button-counter>
等价于<button-counter v-bind="post"></button-counter>
2、通过插槽传递
app.component('button-counter', {
template: `<slot></slot>`
})
<button-counter>相信自己</button-counter>
子组件渲染出来的slot元素就是相信自己