序言:乱七八糟一锅粥!
基于Vue.js 教程、介绍— Vue.js
心得:
- 在vue中,推荐始终使用 kebab-case 的事件名,因为html对大小写不敏感,也就是说,默认会转为全小写。其他像组件名等也使用kebab-case。
- 浏览器渲染次数越少肯定性能越高
- v-html是解析成html,而大括号是解析成字符串。
- “Mustache”语法 (双大括号)
- 所谓的模板应该就是html与vue结合出来的东西
- 实例一个vue实例时,经常起名叫vm,他的意思就是view model
- v-bind的本质作用是将两个属性建立关联
- v-bind:class 指令也可以与普通的 class 属性共存
- html或css的属性名不能用变量表示,属性值可以用变量表示
- 当使用到vue的某一方面的功能时,应该趁机翻阅这一方面功能的全部介绍,以帮助记忆
- 某些第三方库有限制api访问频率的方法,比如_.debounce,侦听器这是一个好例子啊
- 理解什么是钩子函数:就是在某种状态下要执行的函数
- 在vue中慎用箭头函数,因为this的指向往往不是vue实例
- Object.freeze(obj)方法可以破坏响应式
- new Vue时定义的数据属性或方法等,都可以通过vue实例访问到
- 什么叫直接绑定方法?比如
,什么叫内联js语句,比如
- Vue.js 事件处理器这篇文章最后的笔记很好,提到了计算属性里面的方法会先自己执行一下来初始化
- 起包裹作用,以便统一对子元素施加作用。不会在页面显示。
- 参数在指令后以冒号指明。例如,v-bind:href="url",在这里 href 是参数
- 只有当实例被创建时 data 中存在的属性才是响应式的。也就是说new完vue后,再往vm里追加属性是不会响应式的。
- 因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。即:Vue.component()与new Vue()的参数基本一致。一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝,如果 Vue 没有这条规则,多个组件实例之间就会互相影响。
几个指令:
- v-html:于当前元素下生成一个子元素
- v-bind:应用于html的属性;v-bind 缩写:缩写为
- v-on:应用于html的事件监听;v-on 缩写:缩写为
- |:管道符,配合过滤器使用
- v-show:是否显示元素(元素存在于html中)
- v-if:是否生成元素
- v-for:用来循环数组数据,
- ;迭代对象属性值,
- 。你也可以提供第二个的参数为键名,
- {{ key }} : {{ value }} (name : 菜鸟教程)。第三个参数为索引,
- ;迭代整数
- {{ n }} (1到10)
- filter:{{ message | filterA('arg1', arg2) }}这里,message 是第一个参数,字符串 'arg1' 将传给过滤器作为第二个参数, arg2 表达式的值将被求值然后传给过滤器作为第三个参数。
修改或新增时非响应之处理(非响应的原因:js的限制;解决方案:vm.$set or Vue.set,还有其他解决方案查看链接详情)
Vue之数组更新检测
对数组的处理方法分为两类:1.变异方法(像push、pop等,改变原数组)2.非变异方法(像filter、slice等,不改变原数组,返回新数组)
由于 JavaScript 的限制,Vue 不能检测以下变动的数组(即非响应式的,也就是说页面不会相应作出更新):
当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength
对象更新检测及注意事项
还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除
Δv-for之key属性:
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。
例子:
条件判断:
在字符串模板中
{{#if ok}}
Yes
{{/if}}
if else
Sorry
Not sorry
else-if
A
B
C
Not A/B/C
用key管理可复用的元素
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。但有时候两个元素一样,我也想分别渲染,这就用到了key
属性,例如:(如果不加key,vue会复用input元素,只渲染第一个)
Class 与 Style 绑定
当在一个自定义组件(即my-component)上使用 class 属性时,这些类将被添加到该组件的根元素(即p)上面。这个元素上已经存在的类不会被覆盖。
例如,如果你声明了这个组件:
Vue.component('my-component', {
template: '
'
})
然后在使用它的时候添加一些 class:
HTML 将被渲染为:
对于带数据绑定 class 也同样适用:
当 isActive
为 truthy 时,HTML 将被渲染成为:
v-bind:style 的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用单引号括起来)来命名:
直接绑定到一个样式对象通常更好,这会让模板更清晰:
-
注意:当 v-bind:style 使用需要特定前缀的 CSS 属性时,如 transform ,Vue.js 会自动侦测并添加相应的前缀。
这段引用,也就是说,vue会自动做浏览器兼容呗
计算属性:
Δ计算属性不能用在v-for中
Δ计算属性 computed(注意set方法,重点查看computed setter示例)
模板内的表达式(即大括号)非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。于是计算属性横空出世。
例如:
{{ message.split('').reverse().join('') }}
就可以使用计算属性进行改写。
计算属性缓存 vs 方法
我们也可以使用方法达到同样的结果,那么它们俩之间的不同是什么?计算属性有缓存机制,如果它所依赖的属性值未改变,计算属性函数不会再次执行。而方法则是每次都执行。
计算属性,默认只提供get方法就可以了,所以可以直接这样写
computed: {
// 计算属性的 getter
reversedMessage: function () { //这个reversedMessage就是属性名,后面跟的方法就是get方法
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
如果需要用到set方法,那就需要改下一下了
computed: {
reversedMessage: {
get: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
},
set: function(){
//逻辑代码
}
}
}
监听属性($watch):
Δ监听属性,就是当监听到属性有改动时,做出什么反应。watch对象创建的监听方法,方法名应该和model名一样吧?
带$
符号的,应该是vue实例对象的属性或方法,比如$watch
修饰符(事件修饰符&按键修饰符):
什么叫vue的修饰符呢?,有一些事件处理很通用,与业务逻辑没关系,vue就将其提取出来统一处理了。vue有事件修饰符和键盘修饰符,详见Vue.js 事件处理器文章介绍。(另外也要参考这篇好文章:彻底弄懂JS的事件冒泡和事件捕获 和 Vue 事件修饰符 详解)
使用修饰符时,顺序很重要。
可以通过全局 config.keyCodes
对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
修饰符除了可以跟在事件处理后面,也可以跟在v-model后面,.lazy .number .trim 是几个好用的修饰符。Vue.js 表单文章的后面讲到了这几个修饰符。
自动匹配按键修饰符:
你也可直接将
KeyboardEvent.key
暴露的任意有效按键名转换为 kebab-case 来作为修饰符:
也就是说把浏览器的内置按键名当做修饰符,比如实现page down功能的按键,在浏览器中名字为PageDown
,那么我们就可以这样用:
浏览器兼容:有一些按键 (.esc 以及所有的方向键) 在 IE9 中有不同的 key 值, 如果你想支持 IE9,它们的内置别名应该是首选。
系统修饰键(也就是说组合按键?):
直接上例子:
Do something
注意事项:
请注意修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发 keyup.ctrl。而单单释放 ctrl 也不会触发事件。
注意:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。在 Sun 操作系统键盘上,meta 对应实心宝石键 (◆)。在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。
.exact
修饰符:精准控制
直接看例子:
.capture解析:
.capture事件修饰符
obj1
obj2
obj3
obj4
双向数据绑定:
vue的双向数据绑定是通过html的表单控件元素实现的,能不能通过其他html元素也能实现双向数据绑定呢?或许可以想angular一样,还要借助apply方法吧?这些都是我的猜测。
组件(重点是数据传输):
每个组件必须只有一个根元素
举例来说,组件模板不能是这样的:
{{ title }}
应该像是这样的:
{{ title }}
Vue的组件可分为全局组件与局部组件,通过Vue.component注册的就是一个简单的全局组件,在整个html中都可以使用;在实例选项中注册的组件,只能在这个实例中使用,比如:
new Vue({
el: '#app',
components: {
// 将只在父模板可用
'runoob': Child
}
})
父→子 传数据,使用prop:
- 当组件间需要进行数据传输时,需要用到prop
- 使用v-bind,可以将父组件的model与子组件的prop进行绑定, prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。也就是说,数据传输方向只能是从父→子
-
注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。
也就是说,父→子如果传的是对象,那么在子组件中对对象的修改也会反应到父组件中。
- prop的验证:其实就是对prop的值做了限定呗,参考文章 VueJs组件prop验证简单理解
注意那些 prop 会在一个组件实例创建之前进行验证,所以实例的属性 (如 data、computed 等) 在 default 或 validator 函数中是不可用的。
子→父 传数据,使用自定义事件($emit,$on
):
- Δ原生事件监听,关键词是
$listeners
- Δ
.sync
修饰符:其实是自定义事件的变种,.sync
是关键字。
将 v-bind.sync 用在一个字面量的对象上,例如 v-bind.sync=”{ title: doc.title }”,是无法正常工作的,因为在解析一个像这样的复杂表达式的时候,有很多边缘情况需要考虑。
- 子组件在代码中使用 $emit(eventName) 触发事件。
- 父组件在代码中使用 $on(eventName) 监听(或者叫接收)事件,或者在html中使用v-on:eventName 监听(或者叫接收)事件。
- 如果想在某个组件的根元素上监听一个原生事件。可以使用 .native 修饰 v-on 。例如:
。啥意思?子组件触发了一个click事件(这是个js原生事件),父组件就通过v-on:click.native
获得监听?
解析以上代码:
注意这里的
is="todo-item"
属性。这种做法在使用 DOM 模板时是十分必要的,因为在元素内只有
元素会被看作有效内容。这样做实现的效果与
相同,但是可以避开一些潜在的浏览器解析错误。查看 DOM 模板解析说明 来了解更多信息。
简单来说就是:如果在ul元素下插入我们的自定义组件todo-item,可能会遇到某些浏览器解析错误的风险。我们还是在ul中插入li,然后告诉浏览器li就是todo-item,也就是is="todo-item",结果和在ul中插入todo-item的效果完全一样
Prop之传入一个对象的所有属性
如果你想要将一个对象的所有属性都作为 prop 传入,你可以使用不带参数的 v-bind (取代 v-bind:prop-name)。例如,对于一个给定的对象 post:
即:用对象的每个属性作为prop
post: {
id: 1,
title: 'My Journey with Vue'
}
下面的模板:
等价于:
Δ非 Prop 的特性
简单来说,就是在组件中定义的属性=属性值
会被根元素继承?
Δ动态组件
就是动态的决定组件名是什么呗,实现方式就是使用component
标签,通过is
属性声明是哪个组件,例如:
。在这个例子中currentTabComponent
是个计算属性,可以动态的决定组件名是什么
一般来说,动态组件的切换,每次都会创建新的实例。你会发现,之前对该组件的操作记录荡然无存了,全部初始化了。我想切换组件的时候,保存之前的操作,怎么办?通过在动态组件上使用 keep-alive
就可以达到该目的,直接上例子:
Δ异步组件
为了减轻服务器压力,何时需要组件,何时创建组件。怎样达到此目的呢?使用工厂函数。也就是说,当我需要的时候,我告诉工厂,帮我产生一个组件。
如何使用异步组件呢?仔细阅读该链接
局部组件的嵌套使用
摘要:
//假设这是ComponentB
import ComponentA from './ComponentA'
import ComponentC from './ComponentC'
export default {
components: {
ComponentA,
ComponentC
},
// ...
}
这样,ComponentA和ComponentC就可以在ComponentB中使用了
webpack之组件注册(基础组件的全局化自动注册):
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
const requireComponent = require.context(
// 其组件目录的相对路径
'./components',
// 是否查询其子目录
false,
// 匹配基础组件文件名的正则表达式
/Base[A-Z]\w+\.(vue|js)$/
)
requireComponent.keys().forEach(fileName => {
// 获取组件配置
const componentConfig = requireComponent(fileName)
// 获取组件的 PascalCase 命名
const componentName = upperFirst(
camelCase(
// 剥去文件名开头的 `'./` 和结尾的扩展名
fileName.replace(/^\.\/(.*)\.\w+$/, '$1')
)
)
// 全局注册组件
Vue.component(
componentName,
// 如果这个组件选项是通过 `export default` 导出的,
// 那么就会优先使用 `.default`,
// 否则回退到使用模块的根。
componentConfig.default || componentConfig
)
})
几个重要知识点:
- require.context:应该是多模块导入
- fileName.replace(/^./(.*).\w+$/, '$1'):$1代表第一个小括号里匹配到的内容
Δ在组件上使用 v-model
、自定义组件的 v-model
重点阅读
核心:
等价于
核心流程是:里面通过emit把新值告诉外面,外面将新值赋给数据属性,数据属性与prop绑定,再通过prop告诉里面,更新value
Δ输入框 与 单选框或复选框 处理方案不太一样,需要借助model
选项。
Δ插槽:也就是处理在组件的标签间的内容
举例:
Something bad happened.
alert-box
是组件名,‘Something bad happened.’是传递的内容
怎么做?
Vue.component('alert-box', {
template: `
Error!
`
})
关键点:
- 以上例子是匿名插槽,匹配不到的都会放在
的位置 - 具名插槽:组件模板中需要通过
这种方式定义名字,组件调用时,可以通过直接在普通元素上指定名称,如
,也可以使用Here might be a page title
包裹的方式,如
Here might be a page title
- 默认内容:可以预先在组件模板中指定内容,如
,如果在组件使用时,设置了内容,会将默认内容替换。Submit - 如何理解Vue的作用域插槽:基于该链接所示例子思考,为何不在组件模板中的slot中直接做判断,为单数行和奇数行应用不同的样式呢?答:组件提供方不能异想天开的为使用者做决定,组件能做的,只能是把数据抛出去,由使用者决定基于该数据作何处理。关键词:slot-scope;核心思路:父组件中必须要有template元素,且必须有slot-scope属性,slot-scope属性的值可以是临时变量名,接收从子组件中传递上来的属性,属性可以是任意定义的。
直接看例子:
/*
* 这是组件的使用案例
* 注意几个关键点:
* template元素
* slot-scope属性
* slotProps.todo中的todo必须是组件模板中的slot传过来的
*/
✓
{{ slotProps.todo.text }}
那组件模板是咋定义的呢?
-
{{ todo.text }}
- 编译作用域
当你想在插槽内使用数据时,例如:
Logged in as {{ user.name }}
该插槽(指的是 Logged in as {{ user.name }}
)可以访问跟这个模板的其它地方相同的(也就是该slot下的模板)实例属性 (也就是说“作用域”是相同的)。但这个插槽不能访问
的作用域。例如尝试访问 url 是不会工作的。牢记一条准则:
父组件模板(也就是说
navigation-link
的模板内容)的所有东西都会在父级作用域内编译;子组件模板(也就是说slot
的模板内容)的所有东西都会在子级作用域内编译。我的总结:在vue中,父组件与子组件之间的数据通信要格外注意,并不是想当然的根据层级关系,子就能访问到父,需要一些特殊技术处理。
自定义指令(指令的作用应该就是用来进行dom操作的吧?):
- 像组件一样,也分为全局和局部
- 来一个注册全局指令的demo,注意看代码中的注释
Vue.directive('focus', {
// Δ当绑定元素插入到 DOM 中。
inserted: function (el) {
// 元素已经有了,我要对该元素采取什么动作呢
el.focus() // 聚焦元素
}
})
- 定义指令时的钩子函数,比如上例中的inserted就是钩子函数,钩子函数应该指的就是满足什么条件下触发的函数,几个钩子的详情(钩子函数代表啥意思 + 钩子参数)需要好好查阅。
- 有时候我们不需要其他钩子函数,可以简写函数:
Vue.directive('runoob', function (el, binding) {
// 设置指令的背景颜色
el.style.backgroundColor = binding.value.color
})
路由:
- 路由需要载入 vue-router 库(就像angular一样,vue的核心模块与其它功能模块进行分离,vue-router就不是核心模块)
- 通过路由,可以实现SPA功能
- NPM 路由实例(结合webpack),可以在 Github 上下载:https://github.com/chrisvfritz/vue-2.0-simple-routing-example
路由思路:
- HTML部分:使用
进行导航,使用 to
属性指定链接到哪里;使用来指定链接到的资源放到哪里进行渲染 - JS部分:配置路径与组件的对应关系;使用配置对象创建router实例;将router实例配置到vue实例中。
Δ过渡 & 动画:
- 动画实现和angular类似。不过vue的动画实现,需要使用
组件将要实现动画的元素包裹起来。 - css过渡与动画的区别:CSS 动画用法类似 CSS 过渡,但是在动画中 v-enter 类名在节点插入 DOM 后不会立即删除,而是在 animationend 事件触发时删除。
自定义过渡的类名:
- 自定义过渡的类名优先级高于普通的类名,这样就能很好的与第三方(如:animate.css)的动画库结合使用。
我们可以通过以下特性来自定义过渡类名:
- enter-class
- enter-active-class
- enter-to-class (2.1.8+)
- leave-class
- leave-active-class
- leave-to-class (2.1.8+)
示例:
菜鸟教程 -- 学的不仅是技术,更是梦想!!!
注意看transition里面的属性名及属性值:name的值代表了使用自定义过渡类名;enter-active-class的值代表了"animated tada"这些类名定义的样式应用于enter-active这个阶段;同理,leave-active-class的值代表了"animated bounceOutRight"这些类名定义的样式应用于leave-active这个阶段。
使用钩子函数定义过渡或动画某个阶段的行为
这些钩子函数可以结合 CSS transitions/animations 使用,也可以单独使用。
当只用 JavaScript 过渡的时候,在 enter 和 leave 中必须使用 done 进行回调。否则,它们将被同步调用,过渡会立即完成。
推荐对于仅使用 JavaScript 过渡的元素添加 v-bind:css="false",Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响
初始渲染的过渡:
可以通过 appear 特性设置节点在初始渲染的过渡:指的就是页面载入时自动触发呗?
Δ多个元素的过渡:不懂
未完待续
接下来学习处理边界情况
本文会随着学习的不断深入随时更新