vue2笔记

文章目录

vue基础知识

初识:

image-20211024201322994

image-20211021200818150

Vue实例跟容器是一对一的关系。

尝试2容器1vue实例 vue实例只能接管前面一个 后面一个容器管不了

<h1 id="title">nihao,{{name}}</h1>
<h1 id="title">nihao,{{name}}</h1>
<script>
    new Vue({
    el:'#title',//尝试用了class 效果也是一样的 只照顾到前面那个
    data:{
        name:'北京'
    }
})
</script>

image-20211024195305318

image-20211024193301097

image-20211024201257375

响应式原理

插值语法&模板语法

image-20211024202554630

插值语法:
{{js表达式}}

(js表达式可以理解成一个用变量去装的东西,a++,字符串拼接。。。)

插值里面的变量自动获得data对象里面的数据(其实是暴露VM模型的内部结构),data对象类似之前那个windows对象,属性为变量,里面再嵌套对象就按照对象:属性来调用。

模板语法:

通过vue命令改变标签的属性、行为等,用于解析标签。

数据绑定(v-bind、v-model)

image-20211024210221108

简写:

image-20211024210309208

v-bind 单向数据绑定 只能data–>容器

动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。

我的理解是:

v-bind就是动态地绑定dom元素的属性,让dom元素里面的属性的那个值跟表达式一样

eg:

v-bind: 将url当成一个变量 就去Vue实例里面找url属性了

image-20211024202224467

可以简写成:

image-20211024202500300

v-model 双向数据绑定 只能用在表单类元素(有value值的输入类元素)上

其实是对元素的value进行操作

<input class="inputbox" v-model:keywords="keywords" type="text" />

绑定class样式

image-20211025211204572

image-20211025212532533

image-20211025212555931

补充数组操作

image-20211025212844728

el&data的两种写法

image-20211024211510900

​ 可以使用v.$mount(‘css选择器’)来绑定对象

const v=new Vue({
    // el: ".title",//第一种写法
    data: {
        name: "北京",
    },
});
v.$mount('.title');//第二种写法

在写成普通函数声明的时候,函数的this指向vue实例,写成箭头函数的时候函数的this指向全局的window对象

image-20211024211213519

image-20211024211441167

MVVM模型

image-20211024211842287

image-20211024212255841

可以理解成dom是表示层 VueModel是逻辑层 Model是数据层。VM在页面dom元素设置监听,并且绑定数据层data中的数据,当data中数据更新时,将实时更新表示层dom,如果采用双向绑定,dom元素value改变数据层data中的数据也会实时改变。

补充:下面这样写会报Reference ERROR 说找不到alert属性 这是因为表示层dom绑死在VM上了,表示层只能看到VM作用域里面的数据 alert是window的 VM模型里面没有。。不是作用域链可以往外找这样。。可以将window作为内置对象解决(但是Vue实例是可以读到的window的属性的 那个是作用域链 要分清楚捏

image-20211025192800277

数据代理

就是不直接操作对象,使用中间商操作数据

补充:Object.defineProperties(要修改的对象,属性,配置对象)

image-20211024214225710

例子:obj2通过中间商x来操作obj对象

let obj={x:100}
let obj2={y:200}
Object.defineProperty(obj2,'x',{
    get(){
        return obj.x//每次通过obj2访问obj.x都是通过getter去访问的
    },
    set(value){
        obj.x=value//每次通过obj2修改obj.x都是通过getter去访问的
    }
})

image-20211024220448164

VM模型采用的也是数据代理,使用一个对象去new一个vue实例,数据层data中的数据复制到vm模型里面,并进行加工之后使用_data属性存起来。使用数据代理,vm做中间商,好少写个_data。好像还用了观察者模式(?

image-20211024222542550

事件处理

image-20211024225359537

image-20211024225421310

   <button id="root" v-on:click="show">nihao</button> 
const v = new Vue({
    data: {
        name: "北京",
    },
    methods: {//记住不要写成method了 有s
        show:function() {
            console.log("hello");
        },
    },
});
v.$mount("#root");
事件修饰符

image-20211024231000090

事件修饰符可以连用

image-20211025173423315

点击事件

v-on:click

简写:v-on:简写成@

v-on:click===》@click

键盘事件

image-20211024233956163

image-20211025000237543

ctrl+y

image-20211025174004852

计算属性

image-20211025183536694

计算实例通过对Vue现有属性的计算得到新的属性,叫做计算属性。数据源改变,计算属性根据改变的数据源在原来数据的基础上进行修改。

我感觉跟数据代理很像,计算属性的数据源于Vue实例里面的data对象里面的值,双向绑定的。数据源改变,计算属性调用getter更新,注意,不更新绑定的数据就没什么用,不更新数据源计算属性不会变(其实跟页面元素很像耶。。不过计算属性也的的确确是Vue、VC实例里面的属性就是说。

不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值

这也同样意味着下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:

computed: {
  now: function () {
    return Date.now()
  }
}

缓存这两个字很重要,缓存意味着是计算属性先保留了一份数据源的值,等到响应式依赖数据源发生变化的时候才会更新计算属性数据源。

methods的this是整个Vue实例,不加this就找不到的原因已经是不在methods这个对象里面,找上一层一直找到最外层的window也没有(作用域链想起没哇(。 以前的知识不要丢哇

<div id="root"><input type="text" v-model='fn'></br><input type="text" v-model='sn'></br>
full<span>{{full()}}</span>
</div>
<script>

    new Vue({
        el:'#root',
        data:{
            fn:'fn',
            sn:'sn'
        },
        methods:{
            full(){
                return this.fn+'-'+this.sn//为什么要加this才能用 不加this会报错
                //[Vue warn]: Error in render: "ReferenceError: fn is not defined"
            }
        }
    })

</script>

在最外面加了就能过了

fn=2
sn=1
new Vue({
    el:'#root',
    data:{
        fn:'fn',
        sn:'sn'
    },
    methods:{
        full(){
            return fn+'-'+sn
        }
    }
})

姓名例子:

let vm=new Vue({
    el:'#root',
    data:{
        fn:'fn',
        sn:'sn'
    },
    computed:{
        full:{
            get(){
                console.log('getter');
                return this.fn+'-'+this.sn
            },
            set(value){
                const arr=value.split('-')
                this.fn=arr[0]
                this.sn=arr[1] }
        }

    }
})
计算属性简写(只读不改):

image-20211025191806629

计算属性setter

计算属性默认只有 getter,不过在需要时你也可以提供一个 setter:

// ...
computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// ...

现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstNamevm.lastName 也会相应地被更新。

计算属性setter是会更改数据源的。

监视属性 Watch

监视的属性不存在监视没有作用 但是不会报错(f u

image-20211025194059882

image-20211025193839092

watch:{
    keyWord:{
        immediate:true,
            handler(value)//value传的是监控的值的新值,第二个变量传的是之前的值
        {
            this.filPer=this.per.filter((p)=>{//vue管理的函数this应该是vue实例
                return p.id.indexOf(value)!==-1
            })
        }
    }
}
深度监视 deep

监视引用变量的时候,watch默认检测的变量存的地址值,只要对象不变,watch是不会检测到变化的,可开启deep值,检测引用的对象的变化。

image-20211025195748877

image-20211025195626464

例子:

点击btn,obj的bool置反。watch监视的是对象obj,开了deep,能监视到对象里面的改变,但是由于监视的对象没变,所以传给handler的newValue跟oldValue都没有变化,打印的bool值就都是最新那个值啦。

 <div id="root">
      <button @click="sw">{{obj.bool}}</button>
    </div>
new Vue({
    el:"#root",
    data:{
        obj:{
            bool:true
        }
    },
    methods:{
        sw(){
            this.obj.bool=!this.obj.bool
            //   this.obj={}
        }
    },
    watch:{
        obj:{
            deep:true,
            handler(newValue,oldValue){
                console.log(newValue.bool+','+oldValue.bool)
            }
        }
    }
})

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6ebo4jka-1647068009438)(D:/Typora/image-20211025201456438.png)]

监视简写(只使用handler):

那个function别写成箭头函数了,会出this问题(。

image-20211025202154828

Vue原生的数据监视原理

使用观察者模式

image-20211027214915383

Vue.set()

image-20211027220521328

image-20211027223208683

this

image-20211025203551168

image-20211025203623321

补充箭头函数this

要知道js里面只有两种作用域(es5):函数作用域跟全局作用域。es6新增的作用域是对于let关键字而言的,对于其他还是只有两种作用域啦。

箭头函数本质是return语句啦,没有自己的this,所以用的定义时父级的this

如例子中 作用域只有一个:全局window 对象a并不是作用域哦!对象没有自己的作用域,所以不能充当箭头函数的this,套一个普通函数,里面再放箭头函数,移花接木一下估计可以。

var name = 'window'; 

var A = {
   name: 'A',
   sayHello: () => {
      console.log(this.name)
   }
}

A.sayHello();// 还是以为输出A ? 错啦,其实输出的是window

移花接木ver

var A = {
   name: 'A',
   sayHello:function (){
    let a=()=> {
      console.log(this.name)
   }
   a()
   }
}

A.sayHello();//A

普通函数的this可以指向A 是因为自己分割出了一个作用域

var name = 'window'; // 其实是window.name = 'window'

var A = {
   name: 'A',
   sayHello: function(){
      console.log(this.name)
   }
}

A.sayHello();// 输出A

条件渲染(v-if、v-show)

image-20211026184456115

v-show(操作css)

底层通过调整css的display v-show='false’是display:none 元素节点还是在的只不显示了。

<div  v-show="false" id="root" >
    <button  @click="sw">{{obj.bool}}</button>
</div>

image-20211026182856222

v-if(操作dom)

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

条件判断语句,v-if条件为真时才渲染元素,条件为假时是不渲染元素的,所以节点根本就不在dom树里面。

<div  v-show="false" id="root" >
    <button  @click="sw">{{obj.bool}}</button>
</div>

image-20211026183029349

还有v-else-if v-else跟普通的一样 就是形成一组判断了就是说 btw v-else后面跟条件没有用(但是也不会报错就是说

为了形成一组判断 不可以被打断 被打断后的就判断作废 被没有相通的v-if

<div  id="root" >
    <div  v-if="false"></div>
    <button  @click="sw">{{obj.bool}}</button>
    <div v-else-if='true'>11111111</div>
</div>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RtFv9Tzi-1647068009443)(D:/Typora/image-20211026184032244.png)]

v-if 例子2

window.onload = function () {
    //创建一个vue实例
    var app = new Vue({
        el: "#app",
        data: {
            type: "A",
            loginType: "username",
        },
        methods: {
            changeloginType() {
                let self = this;
                if (self.loginType == "username") {
                    self.loginType = "";
                } else {
                    self.loginType = "username";
                }
            },
        },
    });
};
<div id="app">
    <div style="color: red">v-if的简单实用</div>
    <template>
        <div v-if="type == 'A'">A</div>
        <div v-else-if="type=='B'">B</div>
        <div v-else>C</div>
    </template>
    <div style="color: green">v-if的弹框切换</div>
    <template v-if="loginType === 'username'">
        <label>用户名:</label>
        <input placeholder="Enter your username" key="username-input" />
    </template>
    <template v-else>
        <label>密码:</label>
        <input placeholder="Enter your email address" key="email-input" />
    </template>
    <button @click="changeloginType">切换状态</button>
</div>
针对例子补充 template标签:

浏览器不对template进行渲染 渲染时自动丢掉 不会出现在dom节点里面 只能和v-if配合使用 v-show不行

<div>
    <div id="root">
        <template>
            <div v-if="false"></div>
            <button @click="sw">{{obj.bool}}</button>
            <div>11111111</div>
        </template>
    </div>
</div>

image-20211026184653214

而且会导致找不到root 不是外面包东西的原因 好像就是因为template

<div>
    <template>
        <div id="root">
            <div v-show="false"></div>
            <button @click="sw">{{obj.bool}}</button>
            <div>11111111</div>
        </div>
    </template>
</div>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sqyf0ekB-1647068009444)(D:/Typora/image-20211026184944175.png)]

列表渲染(v-for)

基本概念

v-ifv-for 一起使用时,v-for 具有比 v-if 更高的优先级。(但是文档并不推荐两个一起用!)

基于源数据多次渲染元素或模板块。此指令之值,必须使用特定语法 alias in expression,为当前遍历的元素提供别名

首先要明确,v-for写在哪里就是列表渲染哪个,如下面。v-for写在div里面,渲染items.lengthdiv!!!

v-for别名的作用域是自己跟子元素。

<div v-for="item in items"><!--item就是别名-->
  {{ item.text }}
</div>

另外也可以为数组索引指定别名 (或者用于对象的键):

<div v-for="(item, index) in items"></div>
<div v-for="(val, key) in object"></div>
<div v-for="(val, name, index) in object"></div>

v-for 的默认行为会尝试原地修改元素而不是移动它们。要强制其重新排序元素,你需要用特殊 attribute key 来提供一个排序提示(绑定的key就变成数组元素的索引了)(有看到说key不止这种用法)

以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key attribute(文档原文)

<div v-for="item in items" :key="item.id">
  {{ item.text }}
</div>
key注意点:
  1. 建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升(有key性能会好一点?)
  2. 不要使用对象或数组之类的非基本类型值作为 v-forkey。请用字符串或数值类型的值。

v-for就是做循环,就是包装了for循环

image-20211026190528967

v-for可以连着in跟of一起(即支持for..infor...of)

v-for…in遍历数组
<div id="root">
    <ul>
        <li v-for="p,index in person">
            {{p}},{{index}}
        </li>
    </ul>
</div>
new Vue({
    el: "#root",
    data: {
        person:[
            {id:001,name:1},
            {id:002,name:2},
            {id:003,name:3},

        ]
    },
});

image-20211026220329413

v-for…in遍历对象

第一个参数是属性的值,第二个参数是属性名,第三个是index,属性排第几个(只有对象可以这么搞,数组不行,数组第二个一定是index)

<div id="root">
    <ul>
        <li v-for="(value,attr,index) in person">
            {{value}}---{{attr}}---{{index}}
        </li>
    </ul>
</div>
new Vue({
    el: "#root",
    data: {
        person:
        {id:"dsasf",name:"sdgvsdf"},
    },
});

image-20211026221127403

使用v-for…of遍历数组、对象、次数、字符串(后两个基本不用)

遍历次数跟python的for in range一样。

image-20211026190306443

image-20211026190316294

image-20211026190219392

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yAteWrSX-1647068009452)(D:/Typora/image-20211026190235388.png)]

v-for注意点
  1. 在遍历对象时,会按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。

  2. 其实会涉及到一个作用域的问题 v-for迭代的对象在其子作用域内可见,但是在其兄弟作用域是不可见

    <ul> 
         <li v-for="item in items">
            <li>{{items}}</li> 
         </li>
       </ul> 
    

    这样嵌套的<li>编译会编译成

    <ul>    
         <li></li>
         <li>{{items}}</li> 
       </ul> 
    

    由于兄弟是看不见的 所以不能看到那个items 也就出错了

  3. v-for是先做循环 然后再跟着下面的(其实这个就是很普通的一个点,就是要记住v-for就是for就是经过包装的for 可以用来控制虚拟dom节点

  4. <li @mouseenter="changeLiBgc(index)"
     v-for="(groupMenuitem, index) in groupMenuitems"
    :key="index"
    :class="{ cur: currentIndex == index }"></li>
    
    changeLiBgc(index) {
          this.$data.currentIndex = index;
          console.log("index"+index)
          console.log(this.$data.currentIndex)
        },
    

    这个是在写尚品汇的时候的一个小的li标签,是三级联动部分。

    当鼠标移动到li上的时候触发mouseenter事件,函数changeLiBgc(index)执行,传的是li执行v-for的索引,只会传鼠标enter的那个元素的index。按照这个的话,后面就比较好理解,但是我自己说不出个所以然来。(是不是因为mouseenter,传EventTarget传了li的index)

    我觉得可能不能完全按照以前的for循环去理解v-for,我以前学的for循环做完就不会再进行改变了,但是v-for是动态的。

    =》v-for做完循环,将虚拟dom变成真实dom,它就会有各种各样的属性,渲染出来的li对象里面有自己的index,当鼠标放上去的时候触发mouseenter事件,将currentIndex赋值成index,然后classv-bind绑定了,计算{}里面js表达式的值。

key原理
diff

在内存中对key相同的节点进行比较,如果内容一样则将之前渲染过的节点直接拿来用,否则就将放弃掉之前的节点,重新渲染虚拟dom节点成真实dom节点。

总结:

就是使用index作为key的时候(不写key默认将遍历时候的index作为唯一标识),当在前面插入/删除节点时,会造成节点与index的顺序改变 从而造成输入类元素对应不上

如果自定义唯一标识的话,就可以进行更多的重用 只新增新的节点 省去了重新生成旧节点 效率更高

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4xr6IwQ4-1647068009454)(D:/Typora/image-20211026222826520.png)]

image-20211026223107672

image-20211026223314546

补一个用index做key的坏处:

image-20211026224438403

列表过滤

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6ufJExOV-1647068009459)(D:/Typora/image-20211027201206101.png)]

new Vue({

    el:'#root',
    data:{
        keyWord:'',
        per:[{id:'001',name:'aa'},
             {id:'002',name:'bb'},
             {id:'003',name:'cc'},
            ],
        filPer:[]
    },
    watch:{
        keyWord:{
            immediate:true,
            handler(value){
                this.filPer=this.per.filter((p)=>{
                    return p.id.indexOf(value)!==-1
                })
            }
        }
    }
})

image-20211027201502381

补充数组filter

Array.filter(),不会对空数组进行检测,不会改变原始数组。

Array.filter(()=>{
    return 判断条件//这个函数的返回值要是布尔值 要么true 要么false
})//filter返回由旧数组符合条件的值的数构成的新数组

表单收集数据的技巧

image-20211028105334567

其他指令

v-html

image-20211028112127530

v-cloak

防止网速过慢出现插值语法。。

image-20211028124407010

自定义指令

image-20211028154822481

全局指令和局部指令的区别:

全局指令可供整个页面使用,而局部指令只作用于他定义的那个vue实例

全局指令就是直接定义在Vue上,局部就是包在一个vue实例里面

函数式声明

自己敲了一遍 修改name已经不会调用big了(不知道是不是vue3的优化?

调用big应该修改成:

1.第一次解析模板时 被调用

2.指令其绑定的数据源发生改变

const vm=new Vue({
    el: "#root",
    data: {
        n: 1,
        name:"shang"
    },
    directives: {
        big(el, binding)//第一个传的是要操作htmlElement元素 第二个传的是这个绑定的对象
        {
            console.log('big')
            el.innerText=binding.value*10
        },
    },
});

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YOyWisMA-1647068009466)(D:/Typora/image-20211028132122785.png)]

输出的binding:

image-20211028145059250

对象式声明

官方文档的使用函数式获取不了焦点是因为一开始在vue执行指令的时还没有放到页面上 所以element.focus没有用

上面简写的函数式声明,只用了bind、update,没有inserted。自定义指令里面函数的this都是window,并不是vue去维护指令的。其实也好理解,因为自定义指令是直接对dom操作,不经vue手也很正常。

Vue.config.productionTip = false;
const vm=new Vue({
    el: "#root",
    data: {
        n: 1,
        name:"shang"
    },
    directives: {
        big(el, binding) {
            console.log(this)//window
            el.innerText=binding.value*10
        },
        fbind:{
            //指令与元素成功绑定时(元素还在内存 vue还没有将元素放到页面上 简写成函数式的话就是只用bind+update了
            bind(el,binding){
                el.value=binding.value
                console.log(this)//window
            },
            //指令所在元素被插入到页面时
            inserted(el,binding){
                el.focus()
            },
            //指令所在模板被重新解析时(模板重新解析?
            update(el,binding){
                console.log('update')
            }
        }

    }
});
全局指令

写在vue实例里面的是局部指令 只能那个vue实例用 其他实例用不了 写全局指令的话要放到Vue里面(写到构造函数里面 当成属性一起继承使用

生命周期

image-20211031165421717

生命周期图详解:

lifecycle

beforeCreate:初始化生命周期、事件(mothods)但是还没执行数据代理(没有_data _data是undefined!beforeCreate的before是指在数据检测、数据代理之前

created:初始化数据检测、数据代理

然后开始找el、template编译虚拟dom

beforeMount:对dom的操作最终不奏效

在挂载之前,将虚拟dom编译,产生 e l 用 el 用 elel替换掉el

将真实dom挂载到页面上

Mounted:东西都挂好了,页面上显示的是经过vue编译、解析的dom元素 可以对页面上的元素做点想做的操作了。

beforeUpdate:在内存中 已经更新了数据 但是页面上的数据还没更新 还没将更新的元素解析挂到页面上

updated:数据已经更新到页面了 页面数据与内存数据同步

beforeDestroy:可以读到data、method,但是不能够进行修改 因为更新要进行update 已经走到这里了 不能回头走update了

image-20211031190815897

destoryed

摧毁阶段 断掉事件监听、数据检测、跟其他实例的联系 虽然说断掉了事件监听 但是已经绑上的dom事件不会随着vue事件被摧毁而解开 比如给btn绑了点击事件 vue实例destoryed了btn的点击事件还是绑着的

image-20211031175001766

生命周期函数mounted(挂载)

页加载渲染完成之后,会自动执行的方法。

Vue完成模板解析,并将真实dom放到页面(挂载)之后执行,只会执行一次。

Vue.createApp({
    data() {
        return {
            counter: 1,
        };
    },
    mounted(){
        console.log("页面加载完成之后自动执行的函数")
        setInterval(()=>{
            this.counter+=1
        },1000)
    },
    template: "<div>{{counter}}</div>",
}).mount("#app");
<div id="root"><input type="text" v-model='fn'></br><input type="text" v-model='sn'></br>
	full<span>{{full}}</span>
</div>

组件

模块化跟组件化是两种并行的思想。模块化跟组件化都是封装,就是封装内部逻辑,暴露一些接口。

data要用函数式:

对象为引用类型,当重用组件时,由于数据对象都指向同一个data对象,当在一个组件中修改data时,其他重用的组件中的data会同时被修改;而使用返回对象的函数,由于每次返回的都是一个新对象(Object的实例),引用地址不同,则不会出现这个问题

image-20211101221017676

image-20211101223455146

Vue.config.productionTip = false;
//组件
const school = Vue.extend({
    template: `
			<div>
				{{names}}
				<br>
				{{address}}
			</div>
			`,
    data() {
        return {
            names: "dsgfd",
            address: "gz",
        };
    },
});
//反正只是一个配置对象 自动有个判断 其实也调用了 Vue.extend
const school = {
        template: `
                <div>
                    {{names}}
                    <br>
                    {{address}}
                </div>
            `,
        data() {
          return {
            names: "dsgfd",
            address: "gz",
          };
        },
}


new Vue({
    el: "#root",

    components: {//配置项都是对象 要写对 真无语了 我是笨蛋
        schools: school,
    },
});
VueComponent

在写props代码的时候一直没有注册 然后就提示没有使用子组件 原来是因为没有注册 人家不认识 就说没有用!!!

Vue.config.productionTip = false;
//组件
const school = Vue.extend({//在调用Vue.extend()的时候其实自动new VueComponent()来构造组件了
    template: `
		<div>
			{{names}}
			<br>
			{{address}}
		</div>
		`,
    data() {
        return {
            names: "dsgfd",
            address: "gz",
        };
    },
})


new Vue({
    el: "#root",

    components: {//一定要注册 注册才算使用了 不登记谁tm知道你名字 人家不认识啊
        schools: school,
    },
});
console.log(school)

image-20211108161752003

image-20211101225446541

vc跟vm差不多 belike vm删减版

vm-》app(vc头子)-》其他vc 还是子父节点那套

image-20211101230601374

Vc&vm的原型链

为了让vc访问vm上面的方法

VueComponent.prototpye.__proto__=Vue.prototpye

image-20211108164306476

脚手架

image-20211109233809214

image-20211109235254158

image-20211109235316207

render函数
//简写
new Vue({
  render: h => h(App),
}).$mount('#app')
//完整版
new Vue({
  render(createElement) {
    return createElement(App)},
}).$mount('#app')

render(createElement)

渲染函数,根据拿到的虚拟节点渲染到页面上。一定要有返回值,用createElement返回,要拿到节点才能去渲染。

createElement(html元素|组件,元素属性,子节点内容)用于创建虚拟dom元素。约定的简写叫 h, vm中有一个方法 _c, 也是这个函数的别名。在Vue2中,虚拟DOM就是通过一种VNode类表达,每个DOM元素或组件都对应一个VNode对象。

image-20211110132027272

ref

相当于原生的getElementById()

app.vue文件函数里面的this都是vc组件 确实(。不然呢

是vm上的一个属性$refs

show(){
        console.log(this)
      }

image-20211111235656395

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UdUH3scQ-1647068009479)(D:/Typora/image-20211112001253639.png)]

show(){
        console.log(this.$refs.btn)
      }

image-20211111235832673

给html标签加上ref属性之后,就自动加到vc的$refs里面,被收集了。跟以前加id是一样的。

如果加的是组件标签就将组件加到$refs里面

image-20211112001129082

props

首先要明确 props传的东西只是变量里面的值(也就是belike浅复制) 传的只是对象的地址 并不是对象的值(props是只读的)

首先可以实现组件间通信,主要是父子间的通信,实现兄弟间的通信就比较麻烦,要借助父组件,子传父再传兄弟,不如直接使用vuex比较方便。

为什么说是父子组件间通信呢,这主要是通过给组件的属性赋值来传递。要注册组件这必然是父子组件了。父传子:

//home.vue  
<div>
    <three :ismove="true"></three>
 </div>

此外,从子->父的通信 父给子一个函数子调用,传参,数据传回父组件里面。

image-20211112003328740

image-20211112003347711

image-20211112002609512

除了上面的通过props来静态地绑定,也可以通过v-bind动态绑定。

prop 可以通过 v-bind 动态赋值,例如:

<!-- 动态赋予一个变量的值 -->
<blog-post v-bind:title="post.title"></blog-post>

<!-- 动态赋予一个复杂表达式的值 -->
<blog-post
  v-bind:title="post.title + ' by ' + post.author.name"
></blog-post>
传入一个对象的所有 property

如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind (取代 v-bind:prop-name)。例如,对于一个给定的对象 post

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的方法(2种):
  1. **这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。**在这种情况下,最好定义一个本地的 data property 并将这个 prop 用作其初始值:

    props: ['initialCounter'],
    data: function () {
      return {
        counter: this.initialCounter
      }
    }
    
  2. **这个 prop 以一种原始的值传入且需要进行转换。**在这种情况下,最好使用这个 prop 的值来定义一个计算属性:

    props: ['size'],
    computed: {
      normalizedSize: function () {
        return this.size.trim().toLowerCase()
      }
    }
    
注意

在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身****将会影响到父组件的状态。

mixin

mixin的话this是指向组件的 纯纯滴工具人

//组件
<template>
<div>
<h1>子组件</h1>
  <button @click="showname">showname</button>
</div>
</template>

<script>
import {mixin} from './mixin'
export default {
    name:'child',
    props:{
        zsq:{
            type:String,
            default:'zzzsq'
        }
    },
    methods: {
        showzsq(){
            console.log(this.zsq)
        }
    },
    data(){
        return {
            name:'jack'
        }
    },
    mixins:[mixin]//配置项一定是数组 一个也是
}
</script>

//mixin.js
export let mixin={
    methods:{
        showname(){
            console.log(this.name)
        }
    }

}
插件

image-20211113115410965

scoped

image-20211113130226046

在vue单文件组件中,为了防止全局同css类名名样式的污染,vue-loade对单文件组件 <style> 标签增加了scoped属性的处理。原理就是在html标签上添加data-v-xxxxxxxx属性,然后在css类名后添加属性选择器,即利用css类选择 + 属性选择器实现样式局部化。

使用了scoped之后,就加了data-v-xxxxxxxx属性属性而已,避免污染其他的。

image-20220130172553699

todolist总结

image-20211205114516476

本地存储

以键值对的形式保存,只能够保存字符串,对象用json.stringify转成字符串再保存,不然会就直接调用tostring方法没啥意义

sessionStorage是会话存储,打开一次浏览器算是一次会话,关闭浏览器就没有了。

LocalStorage清除:1.调用clear/removeStorage 2.浏览器设置里面清除缓存

image-20211206194744133

组件自定义事件

绑定自定义事件:

有两种调用:

1.通过$emit

sname是自定义事件名 当事件触发是调用demo函数

App.vue

<template>
    <student @sname="demo"></student>
</template>

student.vue

<template>
  <div>
    <span>{{ studentName }}</span>
    <button @click="sendsm">获取学生姓名</button>
  </div>
</template>

<script>
export default {
  name: "student",
  data() {
    return {
      studentName: "李华",
    };
  },
  methods: {
    sendsm() {
      this.$emit('sname',this.studentName);//调用函数 $emit触发sname事件,后面就是给sname传的参,可以传无限多个
    },
  },
};
</script>

2.ref

<script>
export default {
  name: 'App',
  components: {
    school:school,
    student:student
  },
  methods: {
    demo(sn){
      console.log(sn)
    }
  },
  mounted() {
    this.$refs.student.$on("sname",this.demo)//this.refs.student拿到了App上的student子组件 然后通过$on给子组件绑定自定义事件sname,sname触发时调用demo函数
//这里有一个坑 当使用methods里面声明的函数,函数的this一定是methods的那个vue对象
//如果这里写成 这样虽然可以输出 但是app实际上是没有拿到sn的 因为下面这个函数的this指向的是student子组件 
//回调函数也是普通的函数,js里面就函数跟对象可以切割作用域。
//回调函数有自己的this 里面保存的就是调用回调函数的对象=》谁调用,回调函数的this就是谁
      this.$refs.student.$on("sname",function(sn){
      console.log(sn)
      this.sname=sn
      console.log(this)
    })
      //改成箭头函数就行 
      //箭头没有自己的作用域,没有自己的this 所以用mounted的作用域(?用的就是当前组件的作用域,即App
       this.$refs.student.$on("sname",(sn)=>{
      console.log(sn)
      this.sname=sn
      console.log(this)
    })
  },
}
</script>

要加native才会当成原生 默认是自定义事件

image-20211206220146794

解绑:

image-20211206211410597

image-20211206211055389

image-20211206220510161

全局事件总线

能够实现任意组件间通信

我的理解就是在用一个所有人都能看到的中介,也有点像计网里面的总线结构。所有人都在总线(中介)里面做一个自定义事件,a要给b传数据就调用触发b的自定义事件。其实用的也是昨天学的使用自定义事件来实现子传父的那一套。

为了让所有人都能够看见中介使用直接在Vue里面定义。

image-20211207184047528

beforeCreate()这时候还没有初始化完,所以可以在这里放个prototype,来让实例继承$bus。

image-20211207190908227

补充$on:

绑定事件,用法:

 this.$bus.$on("checkTodo",this.checkTodo)

消息订阅与发布

image-20211207195548280

订阅者:

mounted(){pubsub.subscribe('hello',function(msgName,data){
    console.log('hellllo订阅'+msgName+" "+data)
    console.log(this)//undefined
    //使用第三方库的话,普通函数的this就是undefined 毕竟第三方库vue也不知道你用的什么
    //使用箭头函数就可以跳出调用的 直接出去找this 就是这个vc实例了
})

发布者:

methods: {
    sendsm() {
        pubsub.publish('hello','6666666666')//消息名 数据
      	//传的时候传的第一个参数是消息名,第二个参数才是数据..
        //就是回调函数收到第一个参数是消息名,第二个才是数据 
        //改todo案例的时候给我整无语了 一直删不掉 才知道参数收的消息名
    },
},

取消订阅:

image-20211207200200843

todo发布:

 del(id) {
      this.$bus.$emit("delTodo", id);
      // pubsub.publish('delTodo',id,'xx');
    },

todo订阅:

delTodo(id){//这个参数是消息名 谁狠狠无语了。。
    this.todos=this.todos.filter( (todo) =>{
        return todo.id!=id
    })

nextTick

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

image-20211207213958382

补充Vue的异步更新队列

Vue的更新DOM是异步的。数据变化(比如vm.someData = 'new value'),Vue不会立即重新渲染组件,执行DOM的更新。而是把这些要更新的事件放入一个队列里面,并进行去重。在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。

当你设置 vm.someData = 'new value',该组件不会立即重新渲染。当刷新队列时,组件会在下一个事件循环“tick”中更新。这样回调函数将在 DOM 更新完成后被调用。为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)

<div id="example">{{message}}</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: '123'
  }
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
  vm.$el.textContent === 'new message' // true
})

组件内使用:

回调函数中的 this 将自动绑定到当前的 Vue 实例上:

<script>
Vue.component('example', {
  template: '<span>{{ message }}</span>',
  data: function () {
    return {
      message: '未更新'
    }
  },
  methods: {
    updateMessage: function () {
      this.message = '已更新'
      console.log(this.$el.textContent) // => '未更新'
      this.$nextTick(function () {
        //这里的this是vc
        console.log(this.$el.textContent) // => '已更新'
      })
    }
  }
})
</script>

因为 $nextTick() 返回一个 Promise 对象,所以你可以使用新的 ES2017 async/await语法完成相同的事情:

methods: {
  updateMessage: async function () {
    this.message = '已更新'
    console.log(this.$el.textContent) // => '未更新'
    await this.$nextTick()
    console.log(this.$el.textContent) // => '已更新'
  }
}
总结?

这个是在尚硅谷那个项目里面看到的 顺便看了一下vue的响应式原理 才知道vue的更新是异步的

在组件里发请求,我们希望的是等到返回的数据,然后对数据执行v-for,但是由于这个vue对dom更新是异步的。可能由于返回太久,先对没有返回的的数据执行v-for,执行dom更新,对于后面返回的数据无动于衷。使用this.$nextTick的话就可以把操作排到队伍最后面,等到异步完成了再上。

动画

下面这几种才能用vue的动画

  • 条件渲染 (使用 v-if)
  • 条件展示 (使用 v-show)
  • 动态组件
  • 组件根节点

vue的动画用的是transition 的封装组件,就是什么要加上动画效果,就用<transition><\transition>包起来。

如果有多个组件执行动画:

  1. v-for然后用<transition-group><\transition-group>包起来,而且要执行key
  2. div包起来,这样就是还是一个节点

下例(过渡、动画帧):

过渡例子:

<div id="demo">
  <button v-on:click="show = !show">
    Toggle
  </button>
  <transition name="fade">
    <p v-if="show">hello</p>
  </transition>
</div>
.fade-enter-active, .fade-leave-active {
  transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
}

动画帧例子:

<div id="example-2">
  <button @click="show = !show">Toggle show</button>
  <transition name="bounce">
    <p v-if="show">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris facilisis enim libero, at lacinia diam fermentum id. Pellentesque habitant morbi tristique senectus et netus.</p>
  </transition>
</div>
.bounce-enter-active {
  animation: bounce-in .5s;
}
.bounce-leave-active {
  animation: bounce-in .5s reverse;
}
@keyframes bounce-in {
  0% {
    transform: scale(0);
  }
  50% {
    transform: scale(1.5);
  }
  100% {
    transform: scale(1);
  }
}

当插入或删除包含在 transition 组件中的元素时,Vue 将会做以下处理:

  1. 自动嗅探目标元素是否应用了 CSS 过渡或动画,如果是,在恰当的时机添加/删除 CSS 类名。

  2. 如果过渡组件提供了 JavaScript 钩子函数(before-enter,enter,after-enter,enter-cancelled,before-leave,leave,after-leave,leave-cancelled),这些钩子函数将在恰当的时机被调用。

    (当只用 JavaScript 过渡的时候,enterleave 中必须使用 done 进行回调。否则,它们将被同步调用,过渡会立即完成。

    推荐对于仅使用 JavaScript 过渡的元素添加 v-bind:css="false",Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。)

  3. 如果没有找到 JavaScript 钩子并且也没有检测到 CSS 过渡/动画,DOM 操作 (插入/删除) 在下一帧中立即执行。(注意:此指浏览器逐帧动画机制,和 Vue 的 nextTick 概念不同)

过渡的类名

在进入/离开的过渡中,会有 6 个 class 切换。

  1. v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。(我感觉就是插入的那一帧?)
  2. v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
  3. v-enter-to:定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。
  4. v-leave:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
  5. v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
  6. v-leave-to2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。

Transition Diagram

对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>,则 v- 是这些类名的默认前缀。如果你使用了 <transition name="my-transition">,那么 v-enter 会替换为 my-transition-enter

css动画补充:
@keyframes(关键帧)

关键帧 @keyframes at-rule 规则通过在动画序列中定义关键帧(或waypoints)的样式来控制CSS动画序列中的中间步骤。

@keyframes slidein {
  from {
    transform: translateX(0%); 
  }

  to {
    transform: translateX(100%);
  }
}

(mdn)要使用关键帧, 先创建一个带名称的 @keyframes 规则,以便后续使用 animation-name性将动画同其关键帧声明匹配。每个 @keyframes 规则包含多个关键帧,也就是一段样式块语句,每个关键帧有一个百分比值作为名称,代表在动画进行中,在哪个阶段触发这个帧所包含的样式。也可以按任意顺序列出关键帧百分比;它们将按照其应该发生的顺序来处理。

(菜鸟教程)创建动画是通过逐步改变从一个CSS样式设定到另一个。

在动画过程中,您可以更改CSS样式的设定多次。

指定的变化时发生时使用%,或关键字"from"和"to",这是和0%到100%相同。

0%是开头动画,100%是当动画完成。

感觉有点像进度条,from是开始0%处,干什么,然后中间20%、30%干什么,最后是到to干什么。逐渐从0%相似到100%相似。

重复定义

如果关键帧重名的话,以最后一次定义的为准。一个动画在一个时间只会用一个关键帧。一个关键帧(@keyframes)里面有多个一样的百分比,比如有两个20%,就将这两个20%的样式重叠。

配置代理(解决跨域问题)

为了解决跨域问题可以配置一个代理服务器

8080端口跟8080代理服务器的话就不算跨域,代理服务器跟真的要传数据的服务器之间也没什么跨域问题(跨域是ajax

浏览器的一个保护措施

image-20211220223015825

image-20211220223418448

这个public就是8080代理服务器的根目录 如果使用第一种方式的话,在里面找到就直接用自己的,就不进行转发了。

image-20211220223741645

image-20211220224834067

image-20211220224907260

一定要写pathRewrite,不然url就会带api,在后端服务器中根本没这个路径,or拿错(。

插槽

默认插槽

<slot> <\slot>的位置插入自定义标签的

app.vue

image-20211220230749070catagory.vue

image-20211220230526426

具名插槽

image-20211221190130366

允许在自定义标签里面放多个标签 为slot指定名字 然后再放进去

↓已废弃

image-20211221190601072

这种是现在推荐的v-slot写法:但是只支持template标签

image-20211221190709251

作用域插槽

通过给子组件里面的slot插槽绑定数据来传数据给父组件

slot-scope 废弃

推荐 2.6.0 新增的 v-slot

  • 预期function argument expression

  • 用法

    用于将元素或组件表示为作用域插槽。attribute 的值应该是可以出现在函数签名的参数位置的合法的 JavaScript 表达式。这意味着在支持的环境中,你还可以在表达式中使用 ES2015 解构。它在 2.5.0+ 中替代了 scope

    此 attribute 不支持动态绑定。

  • 参考作用域插槽

scope 移除

被 2.5.0 新增的 slot-scope 取代。推荐 2.6.0 新增的 v-slot

用于表示一个作为带作用域的插槽的 <template> 元素,它在 2.5.0+ 中被 slot-scope 替代。

  • 用法:

    除了 scope 只可以用于 <template> 元素,其它和 slot-scope 都相同。

    使用案例:

image-20211221192033937

image-20211221192125792

总结image-20211221192526551

image-20211221192814048

image-20211221192826484

Vue-x

概念

专门在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中的多个组件的共享状态进行集中式的管理(读/写),也是一种组件间的通信方式,且适用于任意组件间通信。

集中式:把所有人集中在一起上课,上课只需要上一遍。

分布式:到每个人的家里面,给每一个人都上一遍课。

适用场景(多个组件要共享统一份数据)
  • 多个视图依赖同一状态

  • 来自不同视图的行为需要变更同一状态

工作原理

image-20211221222737677

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9d4ro2yr-1647068009518)(D:/Typora/20220105180904.png)]

脚手架import机制:

先全局扫描一遍,将import的文件先执行一次

store/index.js

image-20220105205550659

main.js

image-20220105204456981

// 这个文件用于准备Vuex的核心store

import Vue from 'vue'
import Vuex from 'vuex'
// actions响应组件中的动作
const actions={
    // 这里context上下文就是那种的ministore 传两个值 一个ministore一个是value
    plus:function(context,value){
        console.log("actions",context)
        // 这样做的就是一个转发 
        context.commit("plus",value)
    }
    // plus:function(context,value){
    //     console.log("actions")

    // }
}
// mutations用于操作数据(state)
const mutations={
    // this.sum++
    plus:function(state,value){
        // console.log(state,value)
        state.sum++
        console.log(state,value)
        // console.log("mutations")

    }
}
// state储存数据
const state={
    sum:0
}
// 

Vue.use(Vuex)
export default new Vuex.Store({
    actions,
    mutations,
    state
})

image-20220107170843927state

__ob__: Observer这些数据是vue这个框架对数据设置的监控器,一般都是不可枚举的。

网上有很多解决的方案:

第一种__ob__: Observer 是 Vue 对数据监控添加的属性,如果想去掉可以用赋值的方式。例如Object.assign({},this.owner)。 用这种方式也是可以解决。

**第二种:**假设list里面存放的就是那些带有__ob__: Observer的可以用JSON.parse(JSON.stringify(this.list))完美解决

完整案例:

score/index.js

// 这个文件用于准备Vuex的核心store

import Vue from 'vue'
import Vuex from 'vuex'
// actions响应组件中的动作
const actions={
    // 这里context上下文就是那种的ministore 传两个值 一个ministore一个是value
    plus:function(context,value){
        // 这样做的就是一个转发 
        context.commit("plus",value)
    },
    oddplus:function(context,value){
       if(this.state.sum%2){
        // 这样做的就是一个转发 
        context.commit("plus",value)
       }

    }
}
// mutations用于操作数据(state)
const mutations={
    // this.sum++
    plus:function(state,value){
        // console.log(state,value)
        state.sum++
        console.log(state,value)
        // console.log("mutations")

    }
}
// state储存数据
const state={
    sum:0
}
// 

Vue.use(Vuex)
export default new Vuex.Store({
    actions,
    mutations,
    state
})

count.vue

<template>
<div>
    <span>计算结果:{{$store.state.sum}}</span>
  <button @click="plus">+1</button>
  <button  @click="oddplus">奇数+1</button>
</div>
</template>

<script>
export default {
  name:"count",
  data() {
    return {
      // sum:0,
      value:0
    }
  },
  methods: {
    plus(){
     this.$store.dispatch('plus',2)
    // console.log(this.$store)
    },
    oddplus(){
        this.$store.dispatch('oddplus',2)
    }
  },
}
</script>

<style>

</style>
<template>
  <div id="app">
    <count></count>
  </div>
</template>

<script>
import count from './components/count'

export default {
  name: 'App',
  components: {
     count
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

actions
muation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。

Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数

mutation 必须是同步函数。

mutations: {
  someMutation (state) {
    api.callAsyncMethod(() => {
      state.count++
    })
  }
}

devtools需要记录mutation。然后如果mutation里面的函数是异步的话,不能控制异步函数什么时候做回调,也追踪不了回调函数里面的状态的改变,所以devtools根本就记录不了mutation了。

state

vuex使用单一状态树,用一个对象就包含了全部的应用层级状态,作为一个“唯一数据源 (SSOT)”而存在。整个应用只有一个store实例。

state作为构造器选项,定义了所有我们需要的基本状态参数。

getters
image-20220107191416091
map(4个)

记得要在vuex里面进行引入

image-20220107233449309

mapState

就是从state里面取出属性来当计算属性的

image-20220107203852992

数组写法:

computed:{
    //意思是将vuex中的city数据映射到组件的computed属性里
    ...mapState(["city"])
}

对象写法:

意思是将store里面的state里面的city属性映射到computed属性中的currentCity中(map本身就有映射的意思)。即currentCity代表了$store.state.city

computed:{
        ...mapState({
            currentCity:"city"
        })
    }
总结
  computed: mapState({
	// 第一种写法
    count: 'count', 
     // 第二种写法
    sex: (state) => state.sex,
    // 用普通函数this指向vc组件,要注意  
    from: function (state) { 
      return this.str + ':' + state.from
    },
    
    // 注意下面的写法看起来和上面相同,事实上箭头函数的this指针并没有指向vue实例(this是undefined 应该是es6用的严格模式 所以顶层的this是undefined的原因),因此不要滥用箭头函数
    from: (state) => this.str + ':' + state.from

    myCmpted: function () {
      // 这里不需要state,测试一下computed的原有用法
      return '测试' + this.str
    }

  }),

写成函数的都会在用那个属性的时候 函数会调用一下 我感觉是计算属性的原因

mapGettersimage-20220107204059878
mapMutations

要是不传直接写函数名字的话 会默认传一个event 这样就计算不了

 <button @click="increment">+1</button>

我传的是pointerevent

image-20220107234428075

image-20220107234406661

image-20220107213044062

image-20220107212528476

image-20220107211600865

mapActions

image-20220107213418002

小总结

其实就是跟谁交流就map谁 map其实还有一个映射的意思捏

image-20220107214336401

模块化编码

分块暴露

里面记得写一个namespaced用以区分 让外面的人认识它!!注意是namespaced

image-20220107230137019

image-20220107223257247

image-20220107224042212

image-20220107225144758

image-20220107230012629

image-20220107230554090

image-20220107230613602

路由

基本概念

通过切换页面内的部分组件 实现单页面部分内容(组件)的一个切换

image-20220108001008093

image-20220108001008093

使用方法(Vue2)

路由是vue的一个插件 app.use(router)

路由配置:

router/index.js

import VueRouter from 'vue-router'

import change from '../pages/change'
import home from '../pages/home'
import next from '../pages/next'

//这里就是路由配置
const router=new VueRouter({
    //这里写的就是路由规则
    routes:[
        {
            path:'/change',
            component:change
        },
        {
            path:'/home',
            // name:'home',
            component:home,
            //嵌套了多级路由
            children:[{
                path:'next',
                component:next,
            }]
        }
    ]
})

export default router

const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push (location) {
  return originalPush.call(this, location).catch(err => err)
}

模板

router-link

这个是放url来进行跳转的,放在模板里面 最后经过vue-router库的编译会变成a标签。这使得 Vue Router 可以在不重新加载页面的情况下更改 URL。

active-class 点击激活里面配置的css样式

image-20220108180024171

一级路由的写法:

image-20220108175749679

多级路由:

image-20220108214719990

image-20220108221031941

router-view

这个用于指定组件的呈现位置。放在那里就是哪块组件进行变化。

image-20220108175912698

路由注意点

image-20220108205544302

$router$route

1.文档

通过调用 app.use(router),我们可以在任意组件中以 this.$router 的形式访问它,并且以 this.$route 的形式访问当前路由。
所以可以this. r o u t e r 是 全 局 的 路 由 , 每 一 个 组 件 都 能 访 问 到 它 , t h i s . router是全局的路由,每一个组件都能访问到它,this. router访this.route是当前组件的路由。

我的理解:

$router:全局路由的实例,是router构造方法的实例,用于编程式路由导航进行的路由跳转

$route:当前的路由信息对象

$router里面有全局的路由什么的 还有history、mode什么的,而且还能看到路由规则

image-20220208160807968

route我理解成当前的路由对象(也不是页面,就是当前的路由对象)

image-20220208162009045

2.注册完路由组件之后,不管是路由、路由组件、非路由组件上都有$router$route属性

image-20220208160426088

两者之间的联系this. router中的currentRoute(其实就是当前路由、、)就是组件路由的this.route

image-20220208162528236

ajax相关参数

路由的query参数

跳转路由并携带query参数 to的字符串写法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eryYzsk3-1647068009545)(D:/Typora/image-20220111231355329.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iu14Ohn2-1647068009546)(D:/Typora/image-20220111231635073.png)]

image-20220111231721994

image-20220111234825618

params参数

如果使用params必须使用name 不能使用path 因为这个path已经找不到了。原路径预留的path有占位符:(路径参数)。

当一个路由被匹配时,它的 params 的值将在每个组件中以 this.$route.params 的形式暴露出来。

拿这个过去匹配不到

image-20220111235724152

image-20220111235011032

image-20220112000630834

image-20220112000729427

模板

image-20220111234913345

image-20220111234507013

image-20220112000946299

路由命名

一个配置项命一个名

image-20220111232201363

原来的path改成name也可以正常使用 必须要写成对象然后配置name 写成字符串就是默认是路径了

image-20220111232125975

image-20220111233133450

image-20220111233144884

props

以props的形式组件间传参数

image-20220112002448480

image-20220112002811041

params版

image-20220112002854280

query版:

image-20220112003410712

image-20220112003132427

router-link(声明式路由导航)
push

有点像数组那种 一层一层追加再一层一层退出 默认就是push模式

replace

有点像无痕浏览 是采用取代 历史记录只有一条

image-20220112220809301

image-20220112220712468

编程式路由导航(不借助routerlink的路由导航)

不使用router-link的路由导航,用this.$router使用路由器对象来进行路由的切换

image-20220112232706423

// 字符串路径
router.push('/users/eduardo')

// 带有路径的对象
router.push({ path: '/users/eduardo' })

// 命名的路由,并加上参数,让路由建立 url params参数地址栏不显示参数
router.push({ name: 'user', params: { username: 'eduardo' } })

// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })

// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })

image-20220112230430938

负数是后退 正数是前进 我感觉是读栈里面 前进就是往栈尾走 后退就是往后走(。 说一些废话

下面是后退两步(连点两下后退

image-20220112231530172

面试题:

1.路由传递参数(对象写法) path是否可以结合params参数一起使用?

不可以 会报错 params和name一起用

this.$router.push({ path: '/Search/', params:  { keywords:this.keywords } })

Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location: “/Search/”.

image-20220208224913682

2.如何指定params参数可传可不传

image-20220208231935226

3.params传空串

我的url有点问题 明明已经跳转到login了 但是还是显示这个

image-20220208232132940

image-20220208233020466

ps:路由规则是这样的

{
    path: "/Login/:keywords",
    component:Login ,
    name:"Login",
    meta:{
    showFooter:"false"
    }
},

image-20220208233337809

4.路由能不能使用props

可以,

1)布尔值

在接收的路由组件路由规则上加props

{
	path: "/Login/:keywords",
	component:Login ,
	name:"Login",
	meta:{
		showFooter:"false"
	},
     //传布尔值
	props:true
    //传对象
    props:{keywords:"1111111111"}
    //传函数
    props:(route)=>({params:route.params,a:1,b:2})}
},

然后照常使用就ok

<script>
export default {
name:"Login",
props:["keywords"]
}
</script>
解决 错误NavigationDuplicated: Avoided redundant navigation to current location

这个错误主要是已经在这个页面然后路由又要转到这个页面导致的

goSearch() {
      console.log("goSearch");
      let re = this.$router.push(
        { name: "Login", params: { keywords: this.keywords } }
        // ,
        // () => {
        // //   console.log("yes");
        // },
        // () => {
        // //   console.log("no");
        // }
      );
      console.log(re);//Promise对象
    },
 goSearch() {
      console.log("goSearch");
      let re = this.$router.push(
        { name: "Login", params: { keywords: this.keywords } }
        ,
        () => {
           console.log("yes");
           return 1
        },
        () => {
        	console.log("no");
        }
      );
      console.log(re);//undefined
    },

不知道为什么会这样 加了两个函数返回的变成undefined了…

重写push、replace

const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push (location) {
  return originalPush.call(this, location).catch(err => err)
}

缓存路由组件

keep-alive

include里面填的组件名

image-20220113000932633

home.vue

<router-link active-class="active" to="/home/next">next</router-link>   
<keep-alive>
       <router-view></router-view>//缓存的是跳转的那个next组件
   </keep-alive>
路由组件独有的生命周期钩子

image-20220113004004574

image-20220113004013689

image-20220113002300260

next.vue

<script>
export default {
    name:'next',
    beforeDestroy() {
      console.log("next被销毁了")
    },
    activated() {
        console.log("home actttttt")
    },
    deactivated() {
        console.log("home deactttt")
    },
}
</script>
重定向

通过配置route

router/index.js

    {
         //   重定向 访问/的时候,让它定位到
        path:'*',
        redirect:'/home'
    }

也可以写别的path 下面例子是从 /a 重定向到 /b

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' }
  ]
})

甚至是一个方法,动态返回重定向目标:

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: to => {
      // 方法接收 目标路由 作为参数
      // return 重定向的 字符串路径/路径对象
    }}
  ]
})

注意导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上。在下面这个例子中,为 /a 路由添加一个 beforeEnter 守卫并不会有任何效果。(这个写法没有看过 下次遇到写写看

路由守卫

路由前置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rw6CvKZC-1647068009576)(D:/Typora/image-20220113021733886.png)]

meta元数据(就是所谓的路由元信息了) 配置对象里面的不能随便加属性的,自己在配置对象里面加的属性是读不到的 ,会被无视掉 要放属性就放到meta里面

image-20220113022835799

我们称呼 routes 配置(就是router/index.js 暴露的那个全局路由)中的每个路由对象为 路由记录。路由记录可以是嵌套的,因此,当一个路由匹配成功后,它可能匹配多个路由记录。

例如,根据上面的路由配置,/posts/new 这个 URL 将会匹配父路由记录 (path: '/posts') 以及子路由记录 (path: 'new')。

一个路由匹配到的所有路由记录会暴露为 $route 对象(还有在导航守卫中的路由对象)的$route.matched 数组。我们需要遍历这个数组来检查路由记录中的 meta 字段,但是 Vue Router 还为你提供了一个 $route.meta 方法,它是一个非递归合并所有 meta 字段的(从父字段到子字段)的方法。这意味着你可以简单地写

router.beforeEach((to, from) => {
  // 而不是去检查每条路由记录
  // to.matched.some(record => record.meta.requiresAuth)
  if (to.meta.requiresAuth && !auth.isLoggedIn()) {
    // 此路由需要授权,请检查是否已登录
    // 如果没有,则重定向到登录页面
    return {
      path: '/login',
      // 保存我们所在的位置,以便以后再来
      query: { redirect: to.fullPath },
    }
  }
})

image-20220113023154198

路由后置

image-20220113024438468

独享路由守卫(只有前置没有后置)

只对某一个组件进行限制

image-20220113025035585组件内路由守卫

通过路由规则进入退出分别调用beforeRouterEnterbeforeRouterLeave

进入组件跟离开组件与之前的前置/后置路由守卫完全不一样。前置、后置路由守卫是有一点点像钩子,自动启动,然后我觉得组件内路由守卫更像我本人,推一下动一下。。

点了button路由进入home组件才启动,离开home组件 切换才启动

image-20220113152449340

又及 不通过路由规则 直接展示路由组件是不会触发beforeRouterEnterbeforeRouterLeave

eg

直接放home组件没有触发 由于有初始化还是触发了前置、后置路由守卫(beforeEach、afterEach)

image-20220113161315273

image-20220113161258170

总结:

image-20220113024527610

路由器的两种工作模式

可以使用nignx/node的一个中间件完成匹配

image-20220113164217236

image-20220113164043425

ui组件

image-20220113164246355

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值