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
或者计算属性。
// 定义为dataprops: ['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() }})