前端入门之旅——VUE

275 阅读9分钟

一,引入

<script src="vue.js"></script>

二,插入页面

格式:

<div id="app">
</div>//所有内容都需要写在挂载元素的内部;
new Vue({
    el:"#app",
    data:{
        
    },
    methods:{
        
    }
    
})

三,V-for 循环

循环数组:<li v-for="(item,index) in arr">{{item}}</li>//可以取出数组中每一项的值;
循环对象:<li v-for="(value,key) in obj">{{value}}:{{key}}</li>//可以得到对象中每一项的属性和值;  

四,v-test 和v-html

v-text当做内容插入,当做普通文本插入页面。 v-html可以解析成html代码

    inner是一个字符串形式的变量<a href="#">百度</a>
例:<p v-text="inner"> </p>  =><a href="#">百度</a>
    <p v-html="inner"> </p>  =>可访问的链接百度
    

五,v-on 事件绑定

简写成:@+方法名
1,v-on:click=add(); 2, v-on:click="add";
有两个参数分别是内容和事件;

六,v-model

v-model=" data 下的一个变量":value="",
通常绑定在表单元素中,如果是单选框:value通常代表布尔值,
如果是复选框,通常value代表选中的某个特定的值;
页面内容修改时,后台的内容也修改;

七,v-bind:+属性 简写=>:+属性

属性绑定,通常绑定的是变量//写在属性里的值不可以用连字符,通常用驼峰式替换

当VUE中涉及img时
logo在vue对象中的data中,
<img v-pre src="{{ logo }}" alt="">//出错,src会解析双花括号  
<img v-pre :src='logo' alt="">//正确写法
例:类class如果是一个固定的样式时通常写成class:
若类class变成一个变量则需要写成:class="['list',tag?'active':'']"
或者::class="{wrapper: isA, content: isB}",其中wrapper和content是样式,isA,isB是布尔值

八,v-if 和v-show

v-if= " "用来判断是否符合某条件
v-elseif
v-else必须紧邻使用;
v-if 若不符合 ""内的条件值,则不会生成html 解构,
v-show show后通常是布尔值,若不符合条件 仍会生成html结构,只是会display:none;

九,vue.set()修改数组

在html 中可以通过arr.push()实现数组的追加;
可以通过arr.splice()实现对数组的删除;
但是不能单纯的通过更改数组索引对应的值来真正改变数组如:arr[4]=0;
需要借助vue.set(a,b,c)=>a是需要修改的数组,b是数组需要修改的索引值,c是修改后的内容;
es6中assign用于对象的浅拷贝Object.assign(obj1,obj2)obj2替换obj1中相同的属性,新增不含有的属性,返回的是一个新的obj1;

十,.stop和.capture.stop

利用.stop可以阻止事件的冒泡  
<button @click.stop=del()>删除</button>
利用.capture.stop可以阻止事件的冒泡  
 <button @click.capture.stop=del()>删除</button>

十一,computed 计算属性

结果会被缓存,再次调用不会执行,除非依赖的响应式属性变化才会重新计算

十二,watch监听属性

可以检测一个或多个属性值的变化

watch:{
    a:function(new,old){
        
    },
    b:function(new,old){
        
    }
}

十三,生命周期

八个周期:

  • beforeCreat
    实例创建之前,此时,没有el data methods的任何数据 (undefind)

  • created
    实例创建成功,el下的数据(undefined)data下的数据可以得到

  • beforeMount
    el中的元素已经挂载,但内容并未填充

  • mounted
    el中的数据直接显示

  • beforeupdata
    数据更改之前 虚拟dom里的内容会更新 真实dom里不会更新

  • updated
    数据更改完成 虚拟dom里的内容会更新 真实dom里也会更新

  • beforedestroy
    在 vue 实例销毁之前调用,此时实例任然可用

  • destoryed
    在 vue 实例销毁之后调用,vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁  


十四,template 模板

如果template 模板中包含渲染的函数,该模板不会被渲染。
内部只能有一个父容器。

十五,components

1,全局组件

Vue.component('mytpl',{ //全局组件
      template: '<h1>hello world</h1>'
    })
let xm = new Vue({
      el: '#app',
      data: {
        content1: 'hello'
      },
    })  
两个参数:第一个参数是一个string => 组件名称,第二个参数是一个object 配置组件的模版内容

2,局部组件

<template id="temp">
    <p>111</p>   
</template>
new Vue({
      el: '#app',
      data: {
        content1: 'hello'
      },
      components: { // 局部组件
        'mydialg': {
          template: '<p>天气不错</p>',
          props: []
        },
        'tempp': {
          template: '#temp',
          // key-index => keyIndex
          props: ['title','keyIndex', 'indexKey'], //在子组件内通过props属性接收父组件的传参 props是一个array  
          //父组件向子组件传值  
          //子组件向父组件传值时用$emit('父组件中定义的函数名',子组件的数据)
        }
      }
    })

全局组件和局部组件的区别:
全局组件使用范围:可以在页面中任何位置使用,局部组件使用范围:只能在定义它的el中使用,不能再其他位置使用,否则就无法生效

十六,slot

用于占位,使组件中可以插入其他标签到指定的位置
父组件slot属性 对应着 子组件里的slot的name

1,具名组件
例: 
  <tpl>
      <!-- 父组件slot属性 对应着 子组件里的slot的name -->
      <div>
        <span >jajaja</span>
        <button>0000</button>
      </div>
      //父组件创建具名slot
      //方法一(废弃):<button slot="slot2">aaa</button> 
      //方法二:<template v-slot="slot2">
      				<button/>
                </template>
                缩写:v-slot:header 可以被重写为 #header
  </tpl>
 <template id="temp">
    <div>
      <slot></slot>
      <br>
      名字: <input type="text">
      <br>
      <slot name="slot2"></slot>
    </div>
  </template>
2,作用域组件
可以在父组件中得到子组件定义的变量
父组件:<child v-slot:defult="child">child.name</child>
简写:<child v-slot="child"></child>
不能和具名卡槽混用,多个作用域组件时建议不用简写  

十七,自定义指令

自定义指令可以对普通的dom元素进行操作,可以说是抽取一些方法,可以直接挂载到普通元素上(而非组件);

vue自定义指令的两种方式  v-focus;
方法一:注册一个全局自定义组件;
	Vue.directive('focus', {
        inserted: function (el) {
          // 聚焦元素
          el.focus()
        }
    })
方法二:注册一个局部自定义组件,在组件内部,有一个directives对象;
	directives: {
      focus: {
        // 自定义指令
        inserted: function (el) {
          el.focus()
        }
      }
    }  
钩子函数:
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。

钩子函数参数
指令钩子函数会被传入以下参数:(只有el是允许修改的)
el:指令所绑定的元素,可以用来直接操作 DOM。
binding:一个对象,包含以下 property:
    name:指令名,不包括 v- 前缀。
    value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。等号后的数值
    oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
    expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
    arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo";冒号后面的值。
    modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
vnode:Vue 编译生成的虚拟节点。。
oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。 	  
动态指令参数:
指令的参数是可以变化的:通过binding.arg=...;进行修改;
指令的绑定值可以是JS的一切合法表达式;

十八,父组件监听子组件是否数据挂载

子组件mounted生命周期时,说明子组件的数据已经挂载完毕,父组件可以监听子组件中的mounted数据判断子组件上数据是否挂载完毕

方法一:
1 // Parent.vue
2 <Child @mounted="doSomething"/>
3 
4 // Child.vue
5 mounted() {
6   this.$emit("mounted");
7 }  
方法二:
1 //  Parent.vue
 2 <Child @hook:mounted="doSomething" ></Child>
 3 
 4 doSomething() {
 5    console.log('父组件监听到 mounted 钩子函数 ');
 6 },
 7 
 8 //  Child.vue
 9 mounted(){
10    console.log('子组件触发 mounted 钩子函数 ');
11 },      
@hook还可以监听其他生命周期

十九,过滤器

Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。
过滤器应该被添加在 JavaScript 表达式的尾部,

过滤器的两种自定义方法:  
	1,全局过滤器:
	Vue.filter("format",function(params){
    	//操作需要接受格式化参数
    })
    2,可以写在组件内部(局部过滤器)
    filters:{
    	format:function(params){
        	//操作需要接受格式化参数
        }
    }
过滤器的引用方法:过滤器会接收第一个值作为参数
	1,使用双花括号引用
       {{ message | format }}
	2,在 `v-bind` 中
       <div v-bind:id="rawId | formatId"></div>
过滤器支持串联
	例如:{message|filter1|filter2};
    将message发给filter1,将filter1的返回值发给filter2;

二十,vue中的diff算法

图片转载: p1-jj.byteimg.com/tos-cn-i-t2…
文章转载: juejin.im/post/684490…
由于更新真实DOM的消耗很大,所以Vue用真实dom生成了一个虚拟dom,每一次当dom中的数据发生变化时,首先调用diff算法,对真实dom和虚拟dom进行比较,然后调用patch算法(只会比较同一层级上的dom元素),为真实dom打补丁;
1,首先判断两个节点是否值得比较(也就是两个节点的标签,注释,type等是否相等,值得比较的化调用patchVnode算法,不值得比较的化 直接用新节点替换(插入,删除)老节点);
2,patchVnode
找到新节点和老节点的el,判断Vnode和oldVnode是否指向同一个对象,如果是,那么直接return
如果他们都有文本节点并且不相等,那么将el的文本节点设置为Vnode的文本节点。
如果oldVnode有子节点而Vnode没有,则删除el的子节点
如果oldVnode没有子节点而Vnode有,则将Vnode的子节点真实化之后添加到el
如果两者都有子节点,则执行updateChildren函数比较子节点
3,updataChildren
遍历新节点和老节点的子节点,进行更新;

二十一,data

为什么vue中的data属性不是以对象的形式存在,而是以函数的方式存在呢?
由于vue可能会有很多个实例,如果以对象的方式存在,没有自己的作用域,当多个实例引用同一个对象时,如果都对对象进行数据操作;可能会造成数据污染;

例:vue.component('component',{
	data:{
    }
})
var component1=new component();
var component2=new component();
当一个实例同时操作data时,相当于操作component.prototype.data;另一个实例中的data也会随之改变;

如果以函数的形式出现,data只在这个作用域中有效;

二十二,双向数据绑定,数组修改下标样式不发生改变

vue中修改数组有三种情况
情况一: 利用“push,pop,shift,unshift,splice,sort,reverse” 等修改原数组双向数据绑定会生效
情况二: 利用“map,filter,slice,concat”等不改变原数组视图中的数组自然不会发生改变;
情况三: 利用数组下标修改数组时,会改变原数组,但是页面中的数组不会发生改变。 解决方法

利用this.$set(obj,index,修改后的值);  

二十三,解决VUEX中页面刷新数据重新初始化的问题

二十四,vue.use()和vue.install();

vue.use方法实现了如下两个步骤
1,检查该插件是否被安装(通常默认的插件已经被安装)
2,未被安装的插件,调用其install方法,将其安装到vue实例中;

二十五,vue中mixin()

mixin全局混入

//定义一个混入对象
export default myMixin
//在组件中使用一个混入对象
import myMixin from ''
var Component = Vue.extend({
  mixins: [myMixin]
})
改对象可以向任何一个vue对象一样,拥有数据,生命周期  
合理合并冲突对象;

二十六,vue中$xxx将方法定义到实例中

1,写一个方法,将其用export default(){ }方法暴露出来
2,引入上述方法,创建一个install对象,如果该方法有installed方法;直接return,否则,床件install方法,将其传入vue实例中去

Object.defineProperties(Vue.prototype, {
        $postLog:{
            get () {
                return postLog;
            }
        }
    });
    第一个参数是要定义或修改的对象,第二个参数定义可枚举的属性或修改属性的对象;
    get()返回值是属性的值
    set()返回值是属性的新值
    

3,在main.js中引入上述对象,Vue.use()这个对象

二十七,动态组件

 <component v-bind:is="currentTabComponent" class="tab"></component>
 currentTabComponent是一个函数,返回值是组件注册到vue上的组件名

二十八,$nextTick

在下一次dom更新之后进行延迟回调,获取更新后的dom的最新值;
使用场景:

1,created中通过接口获取数据改变dom结构时,正常情况下由于dom还没有渲染,会报错,
处理方案:在this.$nextTick中进行dom的操作
2,改变dom元素的数据结构后基于新dom想要做一些操作
(vue数据并不是改变后就立刻更新dom的,而是通过自己的响应机制尽心dom的更新,所以改变dom之后,再次获取这个dom没有立刻改变,需要使用this.$nextTick来判断dom是否发生改变)

二十九,vue内部运行机制原理

创建一个Vue对象 =》 init 将其初始化 =》 complie 解析 =》 compile =》 compile编译可以分成 parse、optimize 与 generate 三个阶段,最终需要得到 render 渲染函数 =》 Object.defineProperty 来实现对对象的「响应式」化,使用getter和setter方法读取和设置属性, =》 render function 会被转化成 VNode,更新时根据patch进行更新,patch中的核心是diff算法,diff 算法是通过同层的树节点进行比较而非对树进行逐层搜索遍历的方式,所以时间复杂度只有 O(n) =》 跟据patch去更新真实dom

parse
parse 会用正则等方式解析 template 模板中的指令、class、style等数据,形成AST。

optimize
optimize 的主要作用是标记 static 静态节点,这是 Vue 在编译过程中的一处优化,后面当 update 更新界面时,会有一个 patch 的过程, diff 算法会直接跳过静态节点,从而减少了比较的过程,优化了 patch 的性能。

generate
generate 是将 AST 转化成 render function 字符串的过程,得到结果是 render 的字符串以及 staticRenderFns 字符串。

objece.defineProperty

function defineReactive (obj, key, val) {
    Object.defineProperty(obj, key, {
        enumerable: true,       /* 属性可枚举 */
        configurable: true,     /* 属性可被修改或删除 */
        get: function reactiveGetter () {
            return val;         /* 实际上会依赖收集,下一小节会讲 */
        },
        set: function reactiveSetter (newVal) {
            if (newVal === val) return;
            cb(newVal);
        }
    });
}
diff:
首先在 oldVnode(老 VNode 节点)不存在的时候,相当于新的 VNode 替代原本没有的节点,所以直接用 addVnodes 将这些节点批量添加到 parentElm 上。
然后同理,在 vnode(新 VNode 节点)不存在的时候,相当于要把老的节点删除,所以直接使用 removeVnodes 进行批量的节点删除即可。
最后一种情况,当 oldVNode 与 vnode 都存在的时候,需要判断它们是否属于 sameVnode(相同的节点)。如果是则进行patchVnode(比对 VNode )操作,否则删除老节点,增加新节点。