vue 学习
箭头函数
function() => {}
js 地位
JavaScript 代码能够间接嵌在网页的任何中央,不过通常咱们都把 JavaScript 代码放到 <head>
中;
能够在同一个页面中引入多个.js 文件,还能够在页面中屡次编写<script>js 代码... </script>
,浏览器依照程序顺次执行。
因为浏览器的平安限度,以 file:// 结尾的地址无奈执行如联网等 JavaScript 代码,最终,你还是须要架设一个 Web 服务器,而后以 http:// 结尾的地址来失常执行所有 JavaScript 代码。
js 分号
JavaScript 并不强制要求在每个语句的结尾加;
数字判断
惟一能判断 NaN 的办法是通过 isNaN()
函数
要特地留神相等运算符 ==。JavaScript 在设计时,有两种比拟运算符:
第一种是 == 比拟,它会主动转换数据类型再比拟,很多时候,会失去十分诡异的后果;
第二种是 === 比拟,它不会主动转换数据类型,如果数据类型不统一,返回 false,如果统一,再比拟。
这不是 JavaScript 的设计缺点。浮点数在运算过程中会产生误差,因为计算机无法准确示意有限循环小数。要比拟两个浮点数是否相等,只能计算它们之差的绝对值,看是否小于某个阈值:
Math.abs(1 / 3 - (1 - 2 / 3)) < 0.0000001; // true
动静语言与动态语言
变量自身类型不固定的语言称之为动静语言,与之对应的是动态语言。
动静参数
<a v-on:[eventName]="doSomething"> ... </a>
当 eventName
的值为 "focus"
时,v-on:[eventName]
将等价于v-on:focus
。
v-bind
v-bind: 属性,单向绑定?
v-on
事件监听,v-on:click=""
,简写 @click=""
2.6.0 新增动静参数,能够依据条件响应不同的事件
须要留神的是,动静表达式中更不要有引号和空格,因为对于 html 的 attribute 名来说这些符号有效,同时浏览器会将 attribute 名转为小写,所以也不要有大写字符
<!-- 变量模式: -->
<template>
<input @[type?up:down]="event1" id="dynamicParam" name="dynamicParam" type="text">
</template>
<script>
export default{data(){
return {
type:false,
up:'keyup',
down:'keydown'
}
}
}
</script>
<!-- 计算属性模式:-->
<template>
<div class="dynamicParamter">
<label for="dynamicParam"> 动静参数 </label>
<input @[type]="event1" id="dynamicParam" name="dynamicParam" type="text">
<button @click="type1=true"> 按键 </button>
<button @click="type1=false"> 键盘抬起 </button>
</div>
</template>
<script>
export default{data(){
return {
href:'href',
url:'',
type1:null,
}
},
methods:{event1(){console.log("触发"+(this.type)+'事件')
}
},
computed:{type(){if(this.type1){return 'keyup'}else{return 'keydown'}
}
}
}
</script>
防抖与节流
有些函数如 mousemove 触发的函数会在短时间内间断调用屡次,如果要使其在一段事件内只执行一次函数,就须要防抖与节流.
防抖:每隔一段事件才触发一次
节流:间断触发后,每隔一段时间才解决一次
// 防抖
function debounce(fn, wait) {
let timeout = null
return function() {if(timeout !== null) clearTimeout(timeout)
timeout = setTimeout(fn, wait);
}
}
function handle() {console.log(Math.random())
}
window.addEventListener('scroll', debounce(handle, 1000))
计算属性
一般来说,模板要求简略的、申明性的,如<span>{{label}}</span>
,然而有时会过于简单如:
<span>{{author.books.length > 0 ? 'Yes' : 'No'}}</span>
尽管代码能够工作,然而过于简单,举荐应用计算属性来申明
<span>{{publishedBooksMessage}}</span>
computed: {
// 计算属性的 getter
publishedBooksMessage() {
// `this` points to the vm instance
return this.author.books.length > 0 ? 'Yes' : 'No'
}
}
计算属性与办法比拟:计算属性在响应式依赖扭转时才会从新求值,而函数在每次调用时都会从新执行
<span>{{countBooks1() }}</span>
<span>{{countBools2}}</span>
methods:{countBooks1() {return books.length}
},
computed:{countBooks2() {return books.length}
}
计算属性能够设置 setter 和 getter
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]
}
}
}
侦听器 watch
能够在响应式数据变动的时候执行函数,相比于计算属性,watch 能够执行异步操作如拜访 api,然而开销比计算属性大
watch: {
// whenever question changes, this function will run
question(newQuestion, oldQuestion) {if (newQuestion.indexOf('?') > -1) {this.getAnswer()
}
}
}
truthiness 真值
在 js 中,除了 false
、0
、""
、null
、undefined
和NaN
之外都为真值。
if (true)
if ({})
if ([])
if (42)
if ("foo")
if (new Date())
if (-42)
if (3.14)
if (-3.14)
if (Infinity)
if (-Infinity)
:class
:class="{active: isActive}"
class 为 isActive
值,当 isActive
为nul
时,没有 class。
此外,:class
能够与 class
属性共存,:class
内也能够有多个值,或者应用数组来提供 class
<div :class="[activeClass, errorClass]"></div>
data() {
return {
activeClass: 'active',
errorClass: 'text-danger'
}
}
:style 语法
:style
的对象语法非常直观——看着十分像 CSS,但其实是一个 JavaScript 对象。CSS property 名能够用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名
条件渲染
条件渲染有两种:v-if
和 v-show
。v-if
只有在条件为真时才渲染条件快,能够和 v-else-if
和v-else
配合应用。v-show
总是会被渲染,虚实值变动体现为 css 的变动
v-for
能够来对应 数组 ,也能够对应 对象 。
对应数组时,两个参数顺次别离示意值和索引;
对应对象时,三个参数顺次别离示意值、键和索引。
<ul id="array-with-index">
<li v-for="(item, index) in items">
{{parentMessage}} - {{index}} - {{item.message}}
</li>
</ul>
<li v-for="(value, name, index) in myObject">
{{index}}. {{name}}: {{value}}
</li>
当 v -for 对应的数据中的数据项扭转了程序,vue 不会挪动 DOM 元素来匹配数据,而是应用“就地更新”的策略,如果想要对应的 DOM 元素扭转地位,须要增加 :key
属性
数组更新检测
数组变更
以下函数能够触发视图更新
push()
pop()
shift()
unshift()
splice()
sort()
-
reverse()
数组替换
以下函数能够保留原来数组,且返回新的数组
filter()
concat()
slice()
显示过滤、排序数组
能够在 v-for
中应用计算属性或者办法
在组件上应用 v -for
因为组件本人独立的作用域,所以在定义组件的时候要应用 props
来传递参数。
<div id="todo-list-example">
<form v-on:submit.prevent="addNewTodo">
<label for="new-todo">Add a todo</label>
<input
v-model="newTodoText"
id="new-todo"
placeholder="E.g. Feed the cat"
/>
<button>Add</button>
</form>
<ul>
<todo-item
v-for="(todo, index) in todos"
:key="todo.id"
:title="todo.title"
@remove="todos.splice(index, 1)"
></todo-item>
</ul>
</div>
const app = Vue.createApp({data() {
return {
newTodoText: '',
todos: [
{
id: 1,
title: 'Do the dishes'
},
{
id: 2,
title: 'Take out the trash'
},
{
id: 3,
title: 'Mow the lawn'
}
],
nextTodoId: 4
}
},
methods: {addNewTodo() {
this.todos.push({
id: this.nextTodoId++,
title: this.newTodoText
})
this.newTodoText = ''
}
}
})
app.component('todo-item', {
template: `
<li>
{{title}}
<button @click="$emit('remove')">Remove</button>
</li>
`,
props: ['title']
})
app.mount('#todo-list-example')
事件处理
若在事件处理中要拜访原始的 DOM 事件,能够用非凡变量 $event 传入办法
<button @click="warn('Test', $event)">Submit</button>
warn(message, event) {if (event) {event.preventDefault()
}
}
一个事件能够有多个办法解决,办法间应用逗号分隔
<button @click="one($event), two($event)">
event.preventDefault() 与 event.stopPropagation()
event.preventDefault()
阻止事件的默认动作发送,比方点击勾选框不能切换勾选状态、在 input 框输出非规定内的字符就无奈输出等等。
event.stopPropagation()
仍会执行默认动作,然而执行完后事件不再被分派到其余节点。
js 冒泡与捕捉
首先咱们须要造成 监听器 的思维。在不应用任何框架的状况下,咱们在 js 中通过 addEventListener
办法给 Dom 增加事件监听。这个办法直译就是增加事件监听器。咱们对 Dom 的操作作为事件会从最里面的先人 Dom 逐渐传递到指标 Dom(捕捉过程),而后再从指标的 Dom 原路传出去(冒泡过程)。通常咱们只监听冒泡过程。在 vue 中,当咱们增加了事件修饰符 capture
后,才会变成捕捉监听器。
所以 vue 中的 .stop
修饰符的进行流传就容易了解了,一般来说在冒泡监听时,是有内层的子 DOM 开始到外层的先人 DOM,设置了 .stop
后,在本 DOM 处理事件后,事件便不再冒泡。
而 .self
能够了解为跳过冒泡事件和捕捉事件,只有间接作用在该元素上的事件才能够执行。
事件修饰符
.stop
执行事件后进行流传.prevent
不执行默认动作,相当于执行event.preventDefault()
.capture
由冒泡监听改为捕捉监听.self
跳过冒泡事件和捕捉事件,只有间接作用在该元素上的事件才能够执行.once
只执行一次.passive
不会执行event.preventDefault()
按键修饰符
应用 @监听键盘事件
<input @keyup.enter="submit" />
.enter
.tab
.delete
(捕捉“删除”和“退格”键).esc
.space
.up
.down
.left
.right
润饰键
润饰键与惯例按键不同,只有在按住 润饰键 的状况下开释其它按键,能力触发事件。而单单开释润饰键也不会触发事件。
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
键盘润饰键
.ctrl
.alt
.shift
.meta
鼠标润饰键
.left
.right
.middle
.exact
修饰符
.exact 修饰符容许你管制由准确的零碎修饰符组合触发的事件。
<!-- 即便 Alt 或 Shift 被一起按下时也会触发 -->
<button @click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何零碎修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>
v-model
对于单选按钮,复选框及抉择框的选项,v-model 绑定的值通常是动态字符串 (对于复选框也能够是布尔值)v-model
有三种修饰符
.lazy
在“change”时而非“input”时更新.number
将用户的输出值转为数值类型,如果这个值无奈被 parseFloat() 解析,则会返回原始的值.trim
主动过滤用户输出的首尾空白字符
组件根底
因为组件是可复用的组件实例,所以它们与 new Vue
接管雷同的选项,例如 data
、computed
、watch
、methods
以及生命周期钩子等。通过 component
能够进行全局注册。
// 定义一个名为 button-counter 的新全局组件
app.component('button-counter', {data() {
return {count: 0}
},
template: `
<button @click="count++">
You clicked me {{count}} times.
</button>`
})
prop
通过 prop
定义属性并向子组件传递数据
const app = Vue.createApp({})
app.component('blog-post', {props: ['title'],
template: `<h4>{{title}}</h4>`
})
app.mount('#blog-post-demo')
监听子组件事件
通过应用 $emit
来传入内部的事件名称,也能够将外部参数传出。
<div id="blog-posts-events-demo">
<div v-bind:style="{fontSize: postFontSize +'em'}">
<blog-post
v-for="post in posts"
:key="post.id"
:title="title"
@enlarge-text="postFontSize += $event"></blog-post>
</div>
</div>
app.component('blog-post', {props: ['title'],
template: `
<div class="blog-post">
<h4>{{title}}</h4>
<button @click="$emit('enlarge-text', 0.1)">
Enlarge text
</button>
</div>
`
})
实现组件的 v -model 双向绑定
为了在组件中能够应用 v-model
,组件须要向外暴露出一个承受的prop
,并且向内规定该数据的getter
和setter
。getter
、setter
能够通过计算属性实现。
<custom-input v-model="searchText"></custom-input>
app.component('custom-input', {props: ['modelValue'],
template: `
<input v-model="value">
`,
computed: {
value: {get() {return this.modelValue},
set(value) {this.$emit('update:modelValue', value)
}
}
}
})
简略插槽
组件中在须要插入的地位应用 <slot>
标签,在应用组件时间接用组件名标签包裹。
<alert-box>
Something bad happened.
</alert-box>
app.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
动静加载组件
应用属性 :is
能够地将简略组件载入到该地位。
<script src="https://unpkg.com/vue@next"></script>
<div id="dynamic-component-demo" class="demo">
<button
v-for="tab in tabs"
v-bind:key="tab"
v-bind:class="['tab-button', { active: currentTab === tab}]"
v-on:click="currentTab = tab"
>
{{tab.name}}
</button>
<component
v-bind:is="currentTab.component"
></component>
</div>
const tabs = [
{
name: 'Home',
component: {template: `<div class="demo-tab">Home component</div>`}
},
{
name: 'Posts',
component: {template: `<div class="demo-tab">Posts component</div>`}
},
{
name: 'Archive',
component: {template: `<div class="demo-tab">Archive component</div>`}
}
]
const app = Vue.createApp({data() {
return {
tabs,
currentTab: tabs[0]
}
},
computed: {currentTabComponent() {return 'tab-' + this.currentTab.toLowerCase()
}
}
})
app.component('tab-home', {template: `<div class="demo-tab">Home component</div>`})
app.component('tab-posts', {template: `<div class="demo-tab">Posts component</div>`})
app.component('tab-archive', {template: `<div class="demo-tab">Archive component</div>`})
app.mount('#dynamic-component-demo')
非凡 DOM 解析注意事项
形如 <table>
、<select>
、<ul>
等等这些标签对于哪些子标签能够呈现在其外部是由严格规定的(比方 <tr>
能够呈现在 <table>
中)。而有些标签如 <li>
、<tr>
和<option>
等只能呈现在特定的元素外部。如果在这些特定标签外部应用自定义的组件,可能会导致出错。此时能够应用 v-is
属性来帮忙解析组件。
<!-- 谬误 -->
<table>
<blog-post-row></blog-post-row>
</table>
<!-- 正确 -->
<table>
<tr v-is="'blog-post-row'"></tr>
</table>
深刻组件
组件命名
倡议应用 kebab-case (短横线分隔命名)
部分注册
部分注册须要援用
import ComponentA from './ComponentA.vue'
export default {
components: {ComponentA}
// ...
}
prop 类型
prop
能够设置类型,在设置谬误时会在控制台提醒用户。
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // 或任何其余构造函数
}
动静传入 Prop
在传入值时,由两种形式:
<!-- 动态赋值 -->
<blog-post title="My journey with Vue"></blog-post>
<!-- 动静赋予一个变量的值 -->
<blog-post :title="post.title"></blog-post>
动态赋值 vue 会视为一个字符串,而 v-bind
或缩写 :
通知 vue 这是一个 js 表达式。
特地地,当一个对象的所有属性都须要传入时,能够应用不带参数的v-bind
。
post: {
id: 1,
title: 'My Journey with Vue'
}
<blog-post v-bind="post"></blog-post>
<!-- 等价于 -->
<blog-post v-bind:id="post.id" v-bind:title="post.title"></blog-post>
单向数据流
咱们曾经晓得,prop
中定义内部传入的数据。如果咱们心愿这些数据成为本地数据,能够供子组件应用,并且在数据更新时,子组件也会更新,与此同时反过来不行(会避免从子组件意外变更父级组件的状态,从而导致你的利用的数据流向难以了解)的话,能够将定义在 prop
中的属性定义为一个 data property
或者计算属性。
// 定义为 data
props: ['initialCounter'],
data() {
return {counter: this.initialCounter}
}
// 定义为计算属性
props: ['size'],
computed: {normalizedSize: function () {return this.size.trim().toLowerCase()}
}
prop 数据验证
能够定义数据类型、指定默认值和自定义验证规定。
app.component('my-component', {
props: {// 根底的类型查看 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function() {return { message: 'hello'}
}
},
// 自定义验证函数
propF: {validator: function(value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
},
// 具备默认值的函数
propG: {
type: Function,
// 与对象或数组默认值不同,这不是一个工厂函数 —— 这是一个用作默认值的函数
default: function() {return 'Default function'}
}
}
})
非 Prop 的 Attribute 继承
非 Prop 的 Attribute,指的是不定义在 prop
中的属性,如 class
、id
、style
等等。在内部应用组件时并应用这些属性时,默认会继承给组件外部的根元素;如果根元素有多个或者想要继承到非根元素,能够应用 v-bind="$attrs"
($attrs
包含组件 props
和emits property
中未蕴含的所有属性 (例如 class
、style
、v-on
监听器等)。)来选定一个 DOM 元素继承。
事件监听器的继承也同理,会默认继承到组件外部根元素。
<!-- 具备非 prop attribute 的 Date-picker 组件 -->
<date-picker data-status="activated"></date-picker>
<!-- 渲染 date-picker 组件 -->
<div class="date-picker" data-status="activated">
<input type="datetime" />
</div>
须要留神的是,如果要使非根节点继承,须要在组件的选项中设置inheritAttrs: false
。
app.component('date-picker', {
inheritAttrs: false,
template: `
<div class="date-picker">
<input type="datetime" v-bind="$attrs" />
</div>
`
})
<!-- Date-picker 组件 应用非 prop attribute -->
<date-picker data-status="activated"></date-picker>
<!-- 渲染 date-picker 组件 -->
<div class="date-picker">
<input type="datetime" data-status="activated" />
</div>
自定义事件
命名举荐应用 kebab-case
(短横线连贯)的事件名,因为v-on
事件监听器在 DOM 模板中会被主动转换为全小写。自定义事件能够和 prop
属性一样进行验证。
app.component('custom-form', {
emits: {
// 没有验证
click: null,
// 验证 submit 事件
submit: ({email, password}) => {if (email && password) {return true} else {console.warn('Invalid submit event payload!')
return false
}
}
},
methods: {submitForm() {this.$emit('submit', { email, password})
}
}
})
v-model 参数与多个 v -model 绑定
在组件中,在 prop
中定义的属性,在内部能够用 v-model: 属性名
的形式来进行多个 v-model
绑定。
自定义 v -model 修饰符
在组件中更能够自定义 v -model 修饰符。然而须要留神的是,修饰符的具体方法是写在属性的 setter 函数中,并且修饰符的名称为arg + "Modifiers"
。
<div id="app">
<my-component v-model:name.capitalize="myText"></my-component>
{{myText}}
</div>
const app = Vue.createApp({data() {
return {myText: ''}
}
})
app.component('my-component', {
props: {
name: String,
nameModifiers: {default: () => ({})
}
},
methods: {emitValue(e) {
let value = e.target.value
if (this.modelModifiers.capitalize) {value = value.charAt(0).toUpperCase() + value.slice(1)
}
this.$emit('update:name', value)
}
},
template: `<input
type="text"
:value="name"
@input="emitValue">`
})
app.mount('#app')
插槽
在组件的模板中能够定义 <slot>
标签,能够替换为字符串、html 代码或其余组件。当 <slot>
标签两头有内容时,这部分内容会作为默认。
<button type="submit">
<slot>Submit</slot>
</button>
具名插槽
在须要多个插槽的状况下,为了辨认不同的插槽,须要给每一个插槽一个名字 name
,不带name
的插槽会有一个隐含的名字“default”。在内部应用组件的具名插槽的时候,应用 <template>
的v-slot
批示插槽名称。留神必须应用 v-slot
时必须在 <template>
标签中。
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
若想让内部拜访组件外部的数据(比方用在插入内容时须要组件外部数据),能够将组件模板中插槽的属性设置为数据。
<slot name="slot1" :item="label"></slot>
<aaa>
<template v-slot:slot1="slotProps">
<p>{{slotProps.item}}</p>
</template>
</aaa>
因为 v-slot
的值是作为函数参数作用的,所以能够写成任何可能作为函数定义中的参数的 JavaScript 表达式。
<!-- 重命名参数 -->
<todo-list v-slot="{item: todo}">
<i class="fas fa-check"></i>
<span class="green">{{todo}}</span>
</todo-list>
<!-- 参数默认值 -->
<todo-list v-slot="{item ='Placeholder'}">
<i class="fas fa-check"></i>
<span class="green">{{item}}</span>
</todo-list>
v-slot
同样能够应用动静参数<template v-slot:[dynamicSlotName]>
具名插槽的缩写
只有在有参数的状况下才能够缩写,应用 #
代替v-slot
。
<todo-list #default="{item}">
<i class="fas fa-check"></i>
<span class="green">{{item}}</span>
</todo-list>
提供与注入
如果在一个多层嵌套的组件链中,内层的组件要取得外层组件的数据,有两种办法:
第一种是后面讲过的设置prop
,然而不不便的是这须要在每一层组件都设置prop
,开发上略微有点麻烦;
第二种是父组件提供数据,子组件注入数据的模式,咱们能够应用 provide
和inject
对。父组件能够作为其所有子组件的依赖项提供程序,而不论组件层次结构有多深。这个个性有两个局部:父组件有一个 provide
选项来提供数据,子组件有一个 inject
选项来开始应用这个数据。
注入的数据默认是非响应式的,如果想要响应式的数据注入,须要调配一个组合式 API computed
属性。
// 父组件提供
app.component('todo-list', {data() {
return {todos: ['Feed a cat', 'Buy tickets']
}
},
provide() {
return {
todoLength1: this.todos.length,
todoLength2: Vue.computed(() => this.todos.length) // 响应式
}
},
template: `
...
`
})
app.component('todo-list-statistics', {inject: ['todoLength'],
created() {console.log(`Injected property: ${this.todoLength1}`) // > 注入 property: John Doe
}
})
保活标签 keep-alive
在一些场景比方切换标签页中,不停地切换标签页会一直地从新渲染组件。应用保活标签 <keep-alive>
能够放弃这些组件的状态,以防止重复重渲染导致的性能问题。
<keep-alive>
<component :is="currentTabComponent"></component>
</keep-alive>
异步组件
不是很懂,目前也临时用不到,等第二次浏览时再细究。
模板援用
如果须要在 js 中拜访组件,能够应用 ref
为组件或 HTML 元素指定援用 ID。留神,因为 $refs
只会在组件渲染实现之后失效,应该防止在模板或计算属性中拜访$refs
。
<input ref="input" />
const app = Vue.createApp({})
app.component('base-input', {
template: `
<input ref="input" />
`,
methods: {focusInput() {this.$refs.input.focus()
}
},
mounted() {this.focusInput()
}
})