Vue核心

快速入门

入门

不写了,参考文档

引入vue.js开发版的时候,控制台会提示,这个提示可以关掉。

Vue.config.productionTip = false

el也可以通过`.$mount(‘#root’)挂载

后面用到了es6的简写,如果不了解es6语法的可以适当补充一下

注意事项

  1. vue不能挂在到html和body上。
  2. vue作用范围包括子元素
  3. 建议使用id选择器
  4. 一个vue实例对应一个容器,一对一
  5. 插值里可以写js表达式,一个表达式可以生成一个值(变量,运算,三元,函数…)
  6. data里面写多个同名属性,会覆盖,只有最后一个有用。
  7. methods里面的最好不要写箭头函数,否则会导致this从vue变成了window。

数据代理

大概是vue的MVVM的原理

Object.defineProperty可以用来给一个对象添加属性,此时添加的属性是具有封装性质的属性,这个属性是否可枚举,可改,可删除都是可以设置的,默认都是false。该函数的参数有三个,分别是对象,属性名,和配置对象。配置对象里可以设置getset函数,来决定该属性的值从哪里获取。而vuedata对象里的东西保存在自己的_data里,然后当访问_data或者修改时,便会调用getset函数,从而达到数据更新的目的。且只有data里面的才会数据代理

我的感觉时重载了.运算符,用户访问vm.name便会调用vm._data.name,但是_data里进行了数据劫持,并不直接提供,而是再度进行封装。总之js的基于对象不是基于类的思路,而是走了原型这条路。具体关于js的面向对象可以参考这里

认识指令

vue主要的一些指令,后面详细解读其原理和更多用法

<h2 v-text="message"></h2> 替换所有

支持字符串拼接

v-html会按照html渲染

v-on:@,可以传参,可以简写@click = "isHot = !isHot"

v-show``v-if 前者只改变display属性

v-bind:src = "xxx":src = "{active:isActive}"可以使用对象形式或者三元表达式

v-model双向绑定,只能用于输入型元素,需要有value属性,另外,v-model:value可以简写为v-model

v-for = "(item, index) in arr"

v-message和某个变量双向绑定

v-cloak一个特殊的属性,没有值,vue在接管以后会删掉这个属性。因此,可以利用css来防止因为网速慢而显示出{{}}这样的插值表达式。cloak意思为斗篷。利用css属性选择器[cloak]:{display: none}就行了。

v-once动态渲染之后,即为静态数据。

v-pre跳过该元素的渲染,如果确定该元素不需要vue,那么加v-pre效率更高

事件修饰符

事件修饰符是在事件名称后直接.一些属性,属于一些简便的写法。下边举几个例子

  1. 可以使用事件修饰符@keyup.enter="myFun"表示回车键,类似的有delete,esc,space,up,down,left等等。特殊的例如CapsLock要用caps-lock。另外tab本身再keydown的时候会切换焦点,所以要想绑定需要用keydown。还有几个比较特殊的键,ctrl,alt,win,shift,当他们与keyup绑定的时候,需要组合键(alt+1)才能出发,keydown正常。如果想用自己的别名,需要再js配置一下,例如给回车键起别名:Vue.config.keyCodes.huiche = 13

  2. 想传参就加括号,不想传参爱写不写!但是写了如果传参,记得多传一个$event,别搞丢了事件。

  3. 再有,event有一个preventDefault()的方法,也可以通过@click.prevent实现。e.target是触发事件的对象

  4. 同理,为了阻止事件冒泡,e.stopPropagation(),也可以用@click.stop代替。一共有六个事件修饰符。

  5. 还有.once表示一次性。

  6. 还可以连着写!@click.stop.prevent@click.ctrl.y

计算属性

计算属性,在Vue的机制里,会通过getter计算出新的属性,最后成为vm的一个属性值,最终的计算属性只是一个属性值,不是对象!具体用法参见下边的代码。通过自动调用getter和setter来达到目的。

需要注意的是,当第一次加载fullName和所依赖的数据如firstName发生改变的时候,fullName会重新调用。如果依赖的数据没有发生变化,得益于vue的缓存机制,fullName的get只会在最开始调用一次,所以性能由于methods。

当修改fullName的值时,set函数调用。

computed:{
fullName:{
get(){

return this.firstName + this.lastName;//这里的this已经被重新定向为vm,切记不要用箭头函数
}
set(){
...
}
}
}

计算属性的简写

下面再来说一下计算属性的简写形式。

当你的计算属性不涉及setter的时候才可以简写,简写代码见下方。

你会发现简写之后的形式,就相当于methods里面的一个函数。但是不同的是,computed里面的函数是Vue调用执行的,最后计算完后成为了vm的一个属性。所以插值引用时不能加括号,这点和直接调用methods里的方法是不同的,且methods里的没有缓存机制和计算属性的一些独有的优化特点。

computed:{
fullName(){
return this.firstName + this.lastName;//这是一种简写形式,但是fullName不能去主动调用
}
}

监视(侦听)属性

监视属性,顾名思义就是当某些属性发生改变的时候,会触发handler。这里说的属性包括上边提到的计算属性。如果写了不存在的属性,那么新旧值都是undefined,不报错。写法如下:

watch:{
isHot:{
//此处举两个配置项
immediate: true, //一上来就调用,此时oldValue是undefined
handler(newValue, oldValue){
...
}
}
}

为了灵活,也可以通过vm的$watch来调用,但是必须在Vue对象实例化之后才能调用

vm.$watch('isHot',{
immediate: true,
handler(newValue, oldValue){
。。。
}
})

深度监视

我们常常遇到这样的问题:当我们要监视的数据不是一个简单的数据类型时(例如一个对象),那么当我们改变了对象的一个属性,对象是否变了呢?本质来说,对象的地址没有变,那么可以认为对象是没有变化的。于是如果我们想监视对象的某个属性就需要用到把上边提到的isHot换成'对象名.属性'这里的引号时必不可少的,具体参见es语法,此处不多赘述。

但是当我们想要监视这个对象的任一属性时,我们直接写该对象时不行的,因为watch默认不会深度监视,所以需要我们打开这个配置,如下

watch:{
一个对象:{
deep: true //打开深度监视
//此处举两个配置项
immediate: true, //一上来就调用,此时oldValue是undefined
handler(newValue, oldValue){
...
}
}
}

监视属性的简写

简写模式和计算属性类似,都需要只有一个函数配置项,这里对于watch和$watch的简写举例说明

watch:{
isHot(newValue, oldValue){
...
}
}

vm.$watch('isHot', function(newValue, oldValue){
...
})

监视与计算的对比

两者某些功能都可以实现,计算属性相对于监视来说不需要再data里面提前定义好属性,所以更加简洁。但是计算属性完全依赖于return的值,所以对于类似定时器等功能无法正常实现,但是监视就方便了。不过需要注意的时,定时函数为什么不写function呢,这就需要了解一下js高级里面的this了,对于function,谁调用的function,this就是谁。但是箭头函数时没有this的,这时this指的就是外层的this,很显然,外层的this就是vm。所以这里不写fuction。

总结:vm管理的函数需要用function,除非不需要用到this,而如果不是vm管理的函数(例如定时器,ajax回调,promise回调),用箭头函数。总之,目的就是让this指向vm。

wahch:{
firstName(val){
setTimeout(()=>{
this.fullName = val + '-' + this.lathName
},1000);
}
}

样式绑定

一个标签如果写多个class属性class='xxx' class='yyy',默认只解析第一个。在vue中,我们可以写class = 'xxx' :class='yyy',两者并不冲突,此时,前者时正常的属性,后者时通过v-bind绑定js表达式,最终由vue一起绑定到class。这里的表达式可以是字符串变量,数组变量和对象,具体格式如下:

//字符串就不写了
//数组也不写了,直接解析里面所有的属性
//对象
classObj{
class1: true, //这里的true绑定一个boolean变量,表示样式的开关
class2: flase
}

style绑定

在上边讲过了class的绑定,这里再说一下style的绑定(虽然用的很少),但是还是介绍一下

:style = "{fontSize: 20px}" //这里需要些样式对象,或者对象数组,注意font-size要写成fontSize

条件渲染

v-show = "js表达式"

v-if

v-else-if 如果上边的v-if满足,那么后面的v-else-if不判断。

v-else 用法我就不用说了吧,猜也能猜到👵不过需要注意的是,通过v-if控制的几条不能被打断。

v-if<template>

当我们需要个一些元素添加样式,又不想一个一个添加,又不想增加父元素,可以用如下的方法,效果实现了,且在dom里面没有template元素,template不能和v-show使用。

<template v-if = "isShow">
<h1>hhh<h1>
<h1>hhh<h1>
</template>

列表渲染

  1. 可以有两个形参,虽然不加括号也行,但是建议加上
  2. key值Vue用来对比前后的虚拟dom的,如果变更以后的虚拟dom和旧的一样,那么就不再重新渲染,而是直接复用,节省效率。所以当你的列表需要变化,例如排序,增删,那么key就非常重要,默认key是index,你需要加一个:key = p.id之类的专用key,防止列表出现问题,尤其是包含input输入框的时候,容易出岔子。
  3. 遍历的对象不一定是数组,也可以是对象,也可以是字符串,具体不多解释,试试就知道了。
<li v-for="(p, index) in persons" :key = "index">
{{p.name}}
</li>

数据监测

这是一块复杂的知识,但是如果不搞懂底层的逻辑,可能会在数据监测中遇到很多问题。首先,我们在data里面写的数据会被加载到vm的_data上,同时,这些属性也会在vm的根节点上,也就是说我们可以直接访问vm.property,然后通过调用get来访问_data里面的属性,_data里的属性同样需要getter,共有两层的getter。为什么要这样呢,前者是为了调用方便,不然每次多要写_data也太不爽了,后者是为了监测数据,你想,当你修改data里的某个数据时,vm如果不知道,那还怎么监测并重新解析数据呢。因此,需要用到_data里面的getter和setter,当你修改数据时,调用了set,从而实现检测的效果。

上边大概说明白了监测实质就是vue将你写在data里面的东西加工了。但是当加工以后,我们还想再往里面添加属性怎么办呢,这就需要深入探讨一下了。

大概分为两种情况,一个是往一个对象里添加属性,一个是往数组里添加数据。

前者,很容易想到obj.name = 'jack'这样的例子,以为我们拿到对象节点就万事大吉,但是不然,这样添加的属性是没有监测效果的,也就是说没有对应的getter和setter,那么怎么办呢,vue提供了解决办法,你可以通过Vue.set(obj, property, value)来添加属性。

后者,我们可以借鉴对象的方式,因为数组毕竟也是个对象,万物皆对象嘛,我们可以将property换成index即可。Vue.set(arr, index, value)。但同时,vue也对数组进行了专门的优化,首先数组里面的每个数据是没有对应的getter和setter的,那么当我们修改数组的数据时,vue怎么判断呢?其实,vue专门对数组的几个函数进行了修改,push pop shift unshift splice sort reverse这几个函数是可以修改原数组的值的,当我们通过这几个函数来修改数组的时候,vue就可以检测到了。切记不要直接通过索引值来修改。

最后说一下细节问题,数组里面如果是对象,那么这个对象的属性是有getter、setter的。

表单收集

我们知道,一想到表单收集,肯定就是v-model了,但是需要注意些特殊的坑,毕竟表单有单选,复选,单行,多行等。

  1. <input type = 'text' v-model='account'/>收集到的是输入的数据
  2. <input type = 'radio' v-model='account' value='sex'/>单选框,收集到的是value,由于没有输入,默认是null,所以需要配置value属性。
  3. <input type = 'checkbox' v-model='account'/>
    1. 如果不配置input,默认手机checked属性,即布尔值
    2. 如果配置
      • 当v-model初始值不是数组,收集的还是checked
      • 否则收集的是value组成的数组。
  4. 修饰符
    1. lazy:失去焦点再收集
    2. number:收集类型转成数字
    3. trim:去除前后空格

自定义指令

简写

其实vue指令本质不就是底层在操作dom嘛,所以你只需要自己写一个指令名,例如v-hello,当你在配置项里配置好hello,那么就可以用了。代码如下

new Vue({
el:
data:
directives{
hello(element, binding)
{
element.innerText = binding.value //就不用多说了吧,两个参数,一个是dom元素,一个是绑定对象,包括了表达式,值等等很多属性,我直呼vue太贴心了。
}
}
})

完整

但是有一些特殊的需求,例如el.focus(),有些命令只能在元素插入到页面以后才能执行。这时候我们就要考虑到自定义指令的调用时机了。

上一个简写模式,只有在最开始绑定以及随后重新解析模板的时候才会调用,也就是没有inserted。并没有所谓的“元素插入页面以后调用”,那么就来看看完全体吧,上代码

new Vue({
el:
data:{}
directives{
hello:
{
bind(ele, bindind){} //绑定时调用
inserted(ele, bindind){} //插入以后调用
update(ele, binding){} //模板更新时调用
}

}
})

细节

  1. vue默认会转成小写,所以命名不要驼峰命名,而是要用v-user-name这样的命名,具体叫啥,我没记住。
  2. 如果想写全局firective,就用Vue.directive('hello', function(){...}),和全局的过滤器是一样的,但是什么是全局过滤器呢,我听了,但是不想写了。反正就是filters filter之类的,好像vue3被弃用了,听听算了。
  3. this是window

生命周期

来了!重点来了。

引入

new Vue({
el:
data:{}
mounted(){ //第一次加载并把元素挂载到页面以后

}
})

介绍

生命周期

细节

  1. 重点关注mounted和beforeDestroy。
  2. 在后者里修改数据不会再触发更新

一些片段

跟着教程敲的一些片段

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>测试</title>
</head>
<body>
<div id="app">
<button @click = "sub">-</button>
<span>{{num}}</span>
<button @click = "add">+</button>
</div>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
var app = new Vue(
{
el:"#app",
data:{
num: 1
},
methods:{
add:function(){
this.num++;
},
sub:function(){
this.num--;
}
}
}
)
</script>
</body>
</html>

<body>
<div id="app">
<span v-show = "isShow">嘿嘿嘿</span>
<button @click = "toggle">显示/隐藏</button>
</div>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
var app = new Vue(
{
el:"#app",
data:{
isShow: true
},
methods:{
toggle:function(){
this.isShow = !this.isShow
},

}
}
)
</script>
</body>

axios

axios回调函数里面的this已经改变,无法在会点函数内部通过this访问data,可以在回调之前拿一个变量保存一下this

axios.get(url).then
(function (response){
console.log(response);
}, function(err){
console.log(err);
})

axios.post(url,data).then
(function (response){
console.log(response);
}, function(err){
console.log(err);
})

组件化

在常规编写中,为了复用,有了模块的概念,也就是分成多个js。而这里的组件化,是撒子呢。看下去

非单文件组件

虽然开发中不太常用,但这里还是写一些基本的用法

用法

const s = Vue.extend(
{
template:`这里写html模板可以多行`
data(){
//data这里不能写对象,而是写函数,因为要拷贝而不是指向同一个
}
})

new Vue({
el:
data:
components:{
school: s //注册组件
}
})

细节

  1. 命名

    • 一个单词可以首字母大写也可以不大写
    • 多个单词可以(kebab-case)也可以大驼峰(CamelCase),但是不在脚手架里,大驼峰是不支持的。
  2. 标签可以写双标签<school></school>,也可以但标签<school/>,但是如果不在脚手架,但标签会导致后续组件无法渲染

  3. 简写

    const school = Vue.extend(options) 可以简写为 const school = options

    原理就是,如果你在传入的是一般对象,那么vue会自动调用extend

嵌套组件

把一个组件当成html,同时把子组件挂载到父组件上。顺序要注意,子组件要在前面。其实每个组件就是一个VueComponent实例,简称vc实例,vc和vm类似,this指向vc,vc是在vm的$children属性里。两个构造函数Vue和VueComponent类似,但是不一样,例如vc里面不能传el,再例如,data在vc里必须是函数,vm里可以是对象。而且正是由于vc是可以复用的,所以data不能写对象。

const b = Vue.extend(
{
template:``
data(){

}

})
const s = Vue.extend(
{
template:`这里写html模板可以多行<b><b/>`//挂在子组件
data(){
//data这里不能写对象,而是写函数,因为要拷贝而不是指向同一个
}
components:{
boy: b
}
})

new Vue({
el:
data:
components:{
school: s //注册组件
}
})

关于原型

在Vue中做了一件事,那就是把VueComponent的原型的原型指向了Vue的原型,而不是Object。具体关于原型的理解,我是这么想的,一个构造函数,本身就是一个对象,他有一个属性是prototype,称为显示原型属性,这个属性里存放的东西可以理解为一个类的静态属性。在这个属性里的东西,所有由该构造函数new的对象通过.__proto__(隐式原型属性)共享。而Vue将VueComponent构造函数的prototype__proto__的指向从Object修改成了Vue的原型对象,以至于组件可以访问Vue中的显示属性。如果还是不太理解,可以去补充一下js高级。js的面向对象和c++和java还是有还很大区别的。

脚手架

终于来了,后缀是.vue,简单说一下组件化的运行逻辑,首先入口是一个html和一个main.js,这个main.js不需要你手动引入,只需要在main.js引入Vue和app组件,然后里面配置好Vue实例,配置好需要的组件(其实只有一个app组件),挂载到对应的html文件上(render)即可。下面是一个组件(.vue)的模板,分为三块,template script style。另外public里面的html文件用到了jsp语法,自己学吧。html里面的<noscript>标签,表示不支持js的时候才解析里面的内容。

<template>
<h1>
{{msg}} //template下必须有一个根节点
</h1>
</template>


<script>
export default{
name: Student, //为了让其它组件引入,需要export

}
</script>


<style>
... 没有就可以不写
</style>

render

由于运行时vue缺少模板渲染,所以用render来替代

render: h => h(App); //vue将会向h传递一个creatElement的函数,来创建元素,且不需要配置component。另外,h的形参传递组件例如App,则如上。如果传html代码,则h('h1','Heloo')。

修改默认配置

想查看Vue-cli的默认配置,只需要控制台vue inspect > output.js,就可以导出所有的配置项目,生成一个js。该文件修改是无效的。如何修改呢?

对于main.js src public index.html favicon.ico就别修改了,能够修改的可以去这里参考一下。具体过程就是在根目录(和package.json同级)创建一个vue.config.js,参照官网的配置项修改。该文件暴露采用common.js,最后和webpack的配置整合。

ref

为了获取元素和组件,vue自定义的一个属性ref,如下当我们给一个元素或者组件加上ref属性以后,就可以通过this.$refs.title拿到这个元素或者组件。和id类似,但是对于组件,通过id拿到的组件是渲染之后的组件而不是组件本体。

<h1 ref = 'title'>
hhh
</h1>

prop

  1. 功能:让组件接收外部传过来的数据(组件传参)

  2. 传递数据:<Demo name="xxx" sex="hhh"/>,这里如果是:name="xxx"那么xxx会按照js表达式解析

  3. 接收数据:

    1. 第一种方式(只接收):props:['name']
    2. 第二种方式(限制类型):props:{name:String}
    3. 第三种方式(限制类型、限制必要性、指定默认值):
    props:{

    name:{

    type:String, //类型

    required:true, //必要性

    default:'老王' //默认值

    }

    }

props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。