Vue2

简介

一套用于构建用户界面的渐进式js框架

特点

1、采用组件化模式,提高代码复用率、且让代码更好维护

2、声明式编码,让编码人员无需直接操作DOM,提高开发效率

3、使用虚拟DOM+优秀算法Diff算法,尽量复用DOM节点

安装

官网:Vue.js

学习——教程——安装——用<script>引入——选择开发版或生产版

使用前准备

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

将文件引入后打开浏览器测试,f12中会出现两个提示,第一个是提示安装开发者工具:

将vue_dev_tools.crx文件拖入到谷歌浏览器的扩展程序(设置—扩展程序)中

安装完毕后如果还是提示就在扩展中打开vue,设置允许访问文件地址!

如果另一个提示不想显示:在官网中点击API,找到全局配置productionTip。在html页面中引入

<script>
    Vue.config.productionTip = false;//阻止Vue在启动时生成生产提示
</script>

插件

Vue3 Snippets:vue代码提示

初识Vue

1、想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象

2、root容器里的代码依然符合html规范,只不过混入了一些特殊Vue语法

3、root容器里面的代码被称为Vue模板

4、Vue实例和容器是一一对应的

5、真实开发中只有一个Vue实例,并且会配合着组件一起使用

6、{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性

7、一旦data中的数据发生改变,那么模板(页面)中用到该数据的地方也会自动更新

注意区分:js表达式和js代码(语句)

1)表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方:

(1). a

(2). a+b

(3). demo()

(4). x === y ? 'a' : 'b'

2)js代码(语句)

(1). if () {}

(2). for () {}

创建Vue实例:

<div id="root">
    你好!{{name}}<!-- 调用改动的值 -->
</div>
<script>
    const vm = new Vue({
        el:'#root',
        data: {//data中用于储存数据,数据供el所指定容器去使用,值可以写成对象或函数
                name: '世界'//存入要改动的值,如果有多个值,用逗号隔开
            }
    });
    //只传入一个参数—配置对象
    //el:'#root':el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串(与id为root的元素建立联系)
    //            或者可以写成document.querySelector('#root');
</script>

el与data的两种写法

el:

const vm = new Vue({
    el:'#root'//第一种
})
console.log(vm);
vm.$mount('#root');//第二种.vm原型上的方法

data:

const vm = new Vue({
    //第一种:对象式
    data:{
        name:'刘德华'
    },
    //第二种:函数式
    data:function(){//简写为data(){}
        console.log(this);//this指向Vue的实例对象vm
        return{
            name:'刘德华'
        }
    }
})
console.log(vm);

由Vue接管的函数,一定不要写成箭头函数,一旦写了箭头函数,this就不再是Vue实例了

模板语法

插值语法:

功能:用于解析标签体内容

写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性

指令语法:

功能:用于解析标签(包括:标签属性,标签体内容,绑定事件....)

举例:v-bind:href="xxx" 或简写为:href="xxx",xxx同样要写js表达式。且可以直接读取到data中的所有属性

备注:Vue中有很多指令,且形式都是:v-xxxx形式

<div id="root">
    <a v-bind:href="url">百度</a>
    <!-- v-bind: 可以简写成 : -->
    <a :href="url">百度</a>
</div>
<script>
    const vm = new Vue({
        el:'#root',
        data: {
                url:'https://www.baidu/'
            }
    });
</script>

数据绑定

Vue中由两种数据绑定的方式:

1、单向绑定(v-bind):数据只能从data流向页面

2、双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data

备注:

(1)双向绑定一般都应用在表单类元素上(如:input、select等)

(2)v-model:value可以简写为v-model,因为v-model默认收集的就是value值

MVVM模型

M:模型(Model):对应data中的数据

V:使徒(View):模板

VM:视图模型(ViewModel):Vue实例对象

备注:

(1)data中所有的属性,最后都出现在vm身上

(2)vm身上的所有属性以及Vue原型上的所有属性,在Vue模板中都可以直接使用

数据代理

Object.definePropery()方法

定义

通过一个对象代理对另一个对象中属性的操作(读/写)

Vue中的数据代理

事件处理

事件的基本使用:

v-on:click简写:@click

<div id="root">
    <h1>学校</h1>
    <button v-on:click="show">
        提示信息
    </button>
</div>
<script>
    const vm = new Vue({
            methods: {
                show() { //show可以传参
                    alert('你好')
                }
            }
        });   
</script>

事件修饰符

1)prevent:阻止默认事件

2)stop:阻止事件冒泡

3)once:事件只触发一次

4)capture:使用事件的捕获模式

5)self:只有event.target是当前操作的元素时才触发事件

6)passive:事件的默认行为立即执行,无需等待事件回调执行完毕

7)native: 组件上也可以绑定原生DOM事件,需要使用native修饰符

修饰符可以连写

<!--阻止默认事件 -->
<div id="root">
    <a href="https://www.baidu" @click.prevent="show">提示信息</a>
</div>
<script>
    const vm = new Vue({
            methods: {
                show() { //show可以传参
                    alert('你好')
                }
            }
        });   
</script>

键盘事件

Vue中常用的按键别名:

回车 => enter

删除 => delete(捕获退格和删除键)

退出 => esc

空格 => space

换行 => tab(特殊,要配合keydown使用)

上 => up

下 => down

左 => left

右 => right

Vue.config.keyCodes.自定义键名 = 键码 可以自定义案件名,如:

Vue.config.keyCodes.huiche = 13

<!-- 也可以使用keycode去指定具体的按键,比如回车的键码是13(不推荐) -->
<input type="text" placeholder="按下回车提示" @keydown.enter="show">
<input type="text" placeholder="按下回车提示" @keydown.13="show">

Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转换为kebab-case(短横线命名--如:CapsLock转换为caps-lock),可以通过e.key获得键名

系统修饰符(用法特殊):ctrl、alt、shift、meta(win键):

(1)配合keyup使用:按下修饰键的同时,再按下其他键,随后释放修饰键,事件才能被触发(如果想绑定按下的另一个键,可以@keyup.ctrl.y

(2)配合keydown使用:正常触发事件

计算属性

conputed

定义:要用的属性不存在,要通过已有属性计算得来

原理:底层借助了Object.defineproperty方法提供的getter和setter

备注:计算属性最终会出现在vm上,直接读取即可({{xxx}})

const vm = new Vue({
    conputed:{
        xxx:{
            get(){//当有人读取了xxx时,get就会被调用,且返回值就是xxx的值
                return 1
            }
            set(){//如果不需要修改可以不写
                
             }
        }
                   }
})

get调用:

1、初次读取xxx时

2、所依赖的数据发生变化时

set调用:当xxx被修改时

简写:

只有只读不修改的时候才可以使用

const vm = new Vue({
    conputed:{
        xxx(){//当做get使用
            
        }
    }
})

监视属性

watch

监视属性watch:

1.当被监视的属性变化时, 回调函数自动调用, 进行相关操作

2.监视的属性必须存在,才能进行监视!!

3.监视的两种写法:

(1).new Vue时传入watch配置

(2).通过vm.$watch监视

深度监视:

(1).Vue中的watch默认不监测对象内部值的改变(一层)。

(2).配置deep:true可以监测对象内部值改变(多层)。

备注:

(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!

(2).使用watch时根据数据的具体结构,决定是否采用深度监视。

<!-- 准备好一个容器-->
        <div id="root">
            <h2>今天天气很{{info}}</h2>
            <!-- 绑定事件的时候:@xxx="yyy" yyy可以写一些简单的语句 -->
            <!-- <button @click="isHot = !isHot">切换天气</button> -->
            <button @click="changeWeather">切换天气</button>
        </div>
    </body>
​
    <script type="text/javascript">
        Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
        
        const vm = new Vue({
            el:'#root',
            data:{
                isHot:true,
                number:{
                    a:1,
                    b:2
                }
            },
            computed:{
                info(){
                    return this.isHot ? '炎热' : '凉爽'
                }
            },
            methods: {
                changeWeather(){
                    this.isHot = !this.isHot
                }
            },
            watch:{//第一种写法
                isHot:{
                    immediate:true, //初始化时让handler调用一下
                    //handler什么时候调用?当isHot发生改变时。
                    handler(newValue,oldValue){
                        console.log('isHot被修改了',newValue,oldValue)
                    }
                }
                //监视多级结构中某个属性的变化
                 'numbers.a':{
                    handler(){
                        console.log('a被改变了')
                    }
                } 
                //监视多级结构中所有属性的变化
                numbers:{
                    deep:true,
                    handler(){
                        console.log('numbers改变了')
                    }
                }
            }
        })
        //第二种写法
        vm.$watch('isHot',{
            immediate:true, //初始化时让handler调用一下
            //handler什么时候调用?当isHot发生改变时。
            handler(newValue,oldValue){
                console.log('isHot被修改了',newValue,oldValue)
            }
        })
    </script>

简写

不使用immediatedeep时使用

isHot(newValue,oldValue){
                    console.log('isHot被修改了',newValue,oldValue)
                }
//第二种
vm.$watch('isHot',(newValue,oldValue)=>{
            console.log('isHot被修改了',newValue,oldValue)
        })

conputed和watch区别

computed和watch之间的区别:

1.computed能完成的功能,watch都可以完成。

2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。

两个重要的小原则:

1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。

2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,

这样this的指向才是vm 或 组件实例对象。

绑定CSS、style样式

:class :style

*绑定样式:

1. class样式

写法:class="xxx" xxx可以是字符串、对象、数组。

字符串写法适用于:类名不确定,要动态获取。

对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。

数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。

2. style样式

:style="{fontSize: xxx}"其中xxx是动态值。

:style="[a,b]"其中a、b是样式对象。

<style>
            .basic{
                width: 400px;
                height: 100px;
                border: 1px solid black;
            }
            
            .happy{
                border: 4px solid red;;
                background-color: rgba(255, 255, 0, 0.644);
                background: linear-gradient(30deg,yellow,pink,orange,yellow);
            }
            .sad{
                border: 4px dashed rgb(2, 197, 2);
                background-color: gray;
            }
            .normal{
                background-color: skyblue;
            }
​
            .atguigu1{
                background-color: yellowgreen;
            }
            .atguigu2{
                font-size: 30px;
                text-shadow:2px 2px 10px red;
            }
            .atguigu3{
                border-radius: 20px;
            }
        </style>
​
​
                                <div id="root">
            <!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
            <div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br/><br/>
​
            <!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
            <div class="basic" :class="classArr">{{name}}</div> <br/><br/>
​
            <!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
            <div class="basic" :class="classObj">{{name}}</div> <br/><br/>
​
            <!-- 绑定style样式--对象写法 -->
            <div class="basic" :style="styleObj">{{name}}</div> <br/><br/>
            <!-- 绑定style样式--数组写法 -->
            <div class="basic" :style="styleArr">{{name}}</div>
                                </div>
​
<script>        
        const vm = new Vue({
            el:'#root',
            data:{
                name:'尚硅谷',
                mood:'normal',
                classArr:['atguigu1','atguigu2','atguigu3'],
                classObj:{
                    atguigu1:false,
                    atguigu2:false,
                },
                styleObj:{
                    fontSize: '40px',
                    color:'red',
                },
                styleObj2:{
                    backgroundColor:'orange'
                },
                styleArr:[
                    {
                        fontSize: '40px',
                        color:'blue',
                    },
                    {
                        backgroundColor:'gray'
                    }
                ]
            },
            methods: {
                changeMood(){
                    const arr = ['happy','sad','normal']
                    const index = Math.floor(Math.random()*3)
                    this.mood = arr[index]
                }
            },
        })
    </script>

条件渲染

条件渲染指令:

  • v-ifv-elsev-else-if

写法:

(1).v-if="表达式"

(2).v-else-if="表达式"

(3).v-else="表达式"

适用于:切换频率较低的场景。

特点:不展示的DOM元素直接被移除。

注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。

  • v-show

v-show

写法:v-show="表达式"

适用于:切换频率较高的场景。

特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉

备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。

<!-- 准备好一个容器-->
        <div id="root">
            <h2>当前的n值是:{{n}}</h2>
            <button @click="n++">点我n+1</button>
            <!-- 使用v-show做条件渲染 -->
            <!-- <h2 v-show="false">欢迎来到{{name}}</h2> -->
            <!-- <h2 v-show="1 === 1">欢迎来到{{name}}</h2> -->
​
            <!-- 使用v-if做条件渲染 -->
            <!-- <h2 v-if="false">欢迎来到{{name}}</h2> -->
            <!-- <h2 v-if="1 === 1">欢迎来到{{name}}</h2> -->
​
            <!-- v-else和v-else-if -->
            <!-- <div v-if="n === 1">Angular</div>
            <div v-else-if="n === 2">React</div>
            <div v-else-if="n === 3">Vue</div>
            <div v-else>哈哈</div> -->
​
            <!-- v-if与template的配合使用 -->
            <template v-if="n === 1">
                <h2>你好</h2>
                <h2>尚硅谷</h2>
                <h2>北京</h2>
            </template>
​
        </div>
​
<script>
        Vue.config.productionTip = false
​
        const vm = new Vue({
            el:'#root',
            data:{
                name:'尚硅谷',
                n:0
            }
        })
</script>

列表渲染

v-for:用于展示列表数据

语法:v-for="(item, index) in xxx" :key="yyy"

可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)

<!-- 准备好一个容器-->
        <div id="root">
            <!-- 遍历数组 -->
            <h2>人员列表</h2>
            <ul>
                <li v-for="(p,index) of persons" :key="index">
                    {{p.name}}-{{p.age}}
                </li>
            </ul>
​
            <!-- 遍历对象 -->
            <h2>汽车信息</h2>
            <ul>
                <li v-for="(value,k) of car" :key="k">
                    {{k}}-{{value}}
                </li>
            </ul>
​
            <!-- 遍历字符串 -->
            <h2>测试遍历字符串(用得少)</h2>
            <ul>
                <li v-for="(char,index) of str" :key="index">
                    {{char}}-{{index}}
                </li>
            </ul>
            
            <!-- 遍历指定次数 -->
            <h2>测试遍历指定次数(用得少)</h2>
            <ul>
                <li v-for="(number,index) of 5" :key="index">
                    {{index}}-{{number}}
                </li>
            </ul>
        </div>
​
<script type="text/javascript">
            Vue.config.productionTip = false
            
            new Vue({
                el:'#root',
                data:{
                    persons:[
                        {id:'001',name:'张三',age:18},
                        {id:'002',name:'李四',age:19},
                        {id:'003',name:'王五',age:20}
                    ],
                    car:{
                        name:'奥迪A8',
                        price:'70万',
                        color:'黑色'
                    },
                    str:'hello'
                }
            })
        </script>

key的原理

1. 虚拟DOM中key的作用:

key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,

随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

2.对比规则:

(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:

①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!

②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。

(2).旧虚拟DOM中未找到与新虚拟DOM相同的key

创建新的真实DOM,随后渲染到到页面。

3. 用index作为key可能会引发的问题:

1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:

会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

2. 如果结构中还包含输入类的DOM:

会产生错误DOM更新 ==> 界面有问题。

4. 开发中如何选择key?:

1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。

2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,

使用index作为key是没有问题的。

列表过滤

<!-- 准备好一个容器-->
        <div id="root">
            <h2>人员列表</h2>
            <input type="text" placeholder="请输入名字" v-model="keyWord">
            <ul>
                <li v-for="(p,index) of filPerons" :key="index">
                    {{p.name}}-{{p.age}}-{{p.sex}}
                </li>
            </ul>
        </div>
​
        <script type="text/javascript">
            Vue.config.productionTip = false
            
            //用watch实现
            new Vue({
                el:'#root',
                data:{
                    keyWord:'',
                    persons:[
                        {id:'001',name:'马冬梅',age:19,sex:'女'},
                        {id:'002',name:'周冬雨',age:20,sex:'女'},
                        {id:'003',name:'周杰伦',age:21,sex:'男'},
                        {id:'004',name:'温兆伦',age:22,sex:'男'}
                    ],
                    filPerons:[]
                },
                watch:{
                    keyWord:{
                        immediate:true,
                        handler(val){
                            this.filPerons = this.persons.filter((p)=>{
                                return p.name.includes(val)
                            })
                        }
                    }
                }
            }) 
            
            //用computed实现
            new Vue({
                el:'#root',
                data:{
                    keyWord:'',
                    persons:[
                        {id:'001',name:'马冬梅',age:19,sex:'女'},
                        {id:'002',name:'周冬雨',age:20,sex:'女'},
                        {id:'003',name:'周杰伦',age:21,sex:'男'},
                        {id:'004',name:'温兆伦',age:22,sex:'男'}
                    ]
                },
                computed:{
                    filPerons(){
                        return this.persons.filter((p)=>{
                            return p.name.indexOf(this.keyWord) !== -1
                        })
                    }
                }
            }) 
        </script>

列表排序

<!-- 准备好一个容器-->
        <div id="root">
            <h2>人员列表</h2>
            <input type="text" placeholder="请输入名字" v-model="keyWord">
            <button @click="sortType = 2">年龄升序</button>
            <button @click="sortType = 1">年龄降序</button>
            <button @click="sortType = 0">原顺序</button>
            <ul>
                <li v-for="(p,index) of filPerons" :key="p.id">
                    {{p.name}}-{{p.age}}-{{p.sex}}
                    <input type="text">
                </li>
            </ul>
        </div>
​
        <script type="text/javascript">
            Vue.config.productionTip = false
            
            new Vue({
                el:'#root',
                data:{
                    keyWord:'',
                    sortType:0, //0原顺序 1降序 2升序
                    persons:[
                        {id:'001',name:'马冬梅',age:30,sex:'女'},
                        {id:'002',name:'周冬雨',age:31,sex:'女'},
                        {id:'003',name:'周杰伦',age:18,sex:'男'},
                        {id:'004',name:'温兆伦',age:19,sex:'男'}
                    ]
                },
                computed:{
                    filPerons(){
                        const arr = this.persons.filter((p)=>{
                            return p.name.indexOf(this.keyWord) !== -1
                        })
                        //判断一下是否需要排序
                        if(this.sortType){
                            arr.sort((p1,p2)=>{
                                return this.sortType === 1 ? p2.age-p1.age : p1.age-p2.age
                            })
                        }
                        return arr
                    }
                }
            }) 
​
        </script>

Vue监视数据原理!!!!

Vue监视数据的原理:

1. vue会监视data中所有层次的数据。

2. 如何监测对象中的数据?

通过setter实现监视,且要在new Vue时就传入要监测的数据。

(1).对象中后追加的属性,Vue默认不做响应式处理

(2).如需给后添加的属性做响应式,请使用如下API:

Vue.set(target,propertyName/index,value) 或

vm.$set(target,propertyName/index,value)

3. 如何监测数组中的数据?

通过包裹数组更新元素的方法实现,本质就是做了两件事:

(1).调用原生对应的方法对数组进行更新。

(2).重新解析模板,进而更新页面。

4.在Vue修改数组中的某个元素一定要用如下方法:

1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()

2.Vue.set() 或 vm.$set()

特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!

收集表单数据

收集表单数据: 若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。 若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。 若:<input type="checkbox"/> 1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值) 2.配置input的value属性: (1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值) (2)v-model的初始值是数组,那么收集的的就是value组成的数组 备注:v-model的三个修饰符: lazy:失去焦点再收集数据 number:输入字符串转为有效的数字 trim:输入首尾空格过滤

<!-- 准备好一个容器-->
        <div id="root">
            <form @submit.prevent="demo">
                账号:<input type="text" v-model.trim="userInfo.account"> <br/><br/>
                密码:<input type="password" v-model="userInfo.password"> <br/><br/>
                年龄:<input type="number" v-model.number="userInfo.age"> <br/><br/>
                性别:
                男<input type="radio" name="sex" v-model="userInfo.sex" value="male">
                女<input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br/><br/>
                爱好:
                学习<input type="checkbox" v-model="userInfo.hobby" value="study">
                打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
                吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">
                <br/><br/>
                所属校区
                <select v-model="userInfo.city">
                    <option value="">请选择校区</option>
                    <option value="beijing">北京</option>
                    <option value="shanghai">上海</option>
                    <option value="shenzhen">深圳</option>
                    <option value="wuhan">武汉</option>
                </select>
                <br/><br/>
                其他信息:
                <textarea v-model.lazy="userInfo.other"></textarea> <br/><br/>
                <input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.atguigu">《用户协议》</a>
                <button>提交</button>
            </form>
        </div>
​
    <script type="text/javascript">
        Vue.config.productionTip = false
​
        new Vue({
            el:'#root',
            data:{
                userInfo:{
                    account:'',
                    password:'',
                    age:18,
                    sex:'female',
                    hobby:[],
                    city:'beijing',
                    other:'',
                    agree:''
                }
            },
            methods: {
                demo(){
                    console.log(JSON.stringify(this.userInfo))
                }
            }
        })
    </script>

过滤器

定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。 语法: 1.注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}} 2.使用过滤器:{{ xxx | 过滤器名}} 或 v-bind:属性 = "xxx | 过滤器名" 备注: 1.过滤器也可以接收额外参数、多个过滤器也可以串联 2.并没有改变原本的数据, 是产生新的对应的数据

<!-- 准备好一个容器-->
        <div id="root">
            <h2>显示格式化后的时间</h2>
            <!-- 计算属性实现 -->
            <h3>现在是:{{fmtTime}}</h3>
            <!-- methods实现 -->
            <h3>现在是:{{getFmtTime()}}</h3>
            <!-- 过滤器实现 -->
            <h3>现在是:{{time | timeFormater}}</h3>
            <!-- 过滤器实现(传参) -->
            <h3>现在是:{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3>
            <h3 :x="msg | mySlice">尚硅谷</h3>
        </div>
​
        <div id="root2">
            <h2>{{msg | mySlice}}</h2>
        </div>
​
    <script type="text/javascript">
        Vue.config.productionTip = false
        //全局过滤器
        Vue.filter('mySlice',function(value){
            return value.slice(0,4)
        })
        
        new Vue({
            el:'#root',
            data:{
                time:1621561377603, //时间戳
                msg:'你好,尚硅谷'
            },
            computed: {
                fmtTime(){
                    return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
                }
            },
            methods: {
                getFmtTime(){
                    return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss')
                }
            },
            //局部过滤器
            filters:{
                timeFormater(value,str='YYYY年MM月DD日 HH:mm:ss'){
                    // console.log('@',value)
                    return dayjs(value).format(str)
                }
            }
        })
    </script>

内置指令

v-bind : 单向绑定解析表达式, 可简写为 :xxx v-model : 双向数据绑定 v-for : 遍历数组/对象/字符串 v-on : 绑定事件监听, 可简写为@ v-if : 条件渲染(动态控制节点是否存存在) v-else : 条件渲染(动态控制节点是否存存在) v-show : 条件渲染 (动态控制节点是否展示) v-text指令: 1.作用:向其所在的节点中渲染文本内容。 2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。 3.v-text不会解析html标签结构

<!-- 准备好一个容器-->
        <div id="root">
            <div>你好,{{name}}</div>
            <div v-text="name"></div>
            <div v-text="str"></div>
        </div>
​
    <script type="text/javascript">
        Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
        
        new Vue({
            el:'#root',
            data:{
                name:'尚硅谷',
                str:'<h3>你好啊!</h3>'
            }
        })
    </script>

v-html :

1.作用:向指定节点中渲染包含html结构的内容。 2.与插值语法的区别: (1).v-html会替换掉节点中所有的内容,{{xx}}则不会。 (2).v-html可以识别html结构。 3.严重注意:v-html有安全性问题!!!! (1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。 (2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!

v-cloak(没有值): 1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。 2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。

<style>
    [v-cloak]{
        display:none;
            }
</style>
            <!-- 准备好一个容器-->
        <div id="root">
            <h2 v-cloak>{{name}}</h2>
        </div>
        <script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>
    
    <script type="text/javascript">
        console.log(1)
        Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
        
        new Vue({
            el:'#root',
            data:{
                name:'尚硅谷'
            }
        })
    </script>

v-once:

1.v-once所在节点在初次动态渲染后,就视为静态内容了。 2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。

v-pre

1.跳过其所在节点的编译过程。 2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。

自定义指令(directives)

一、定义语法: (1).局部指令: new Vue({ new Vue({ directives:{指令名:配置对象} 或 directives{指令名:回调函数} }) }) (2).全局指令: Vue.directive(指令名,配置对象) 或 Vue.directive(指令名,回调函数)

二、配置对象中常用的3个回调: (1).bind:指令与元素成功绑定时调用。 (2).inserted:指令所在元素被插入页面时调用。 (3).update:指令所在模板结构被重新解析时调用。

三、备注: 1.指令定义时不加v-,但使用时要加v-; 2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。

<!--
需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。 
-->
<!-- 准备好一个容器-->
        <div id="root">
            <h2>{{name}}</h2>
            <h2>当前的n值是:<span v-text="n"></span> </h2>
            <!-- <h2>放大10倍后的n值是:<span v-big-number="n"></span> </h2> -->
            <h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
            <button @click="n++">点我n+1</button>
            <hr/>
            <input type="text" v-fbind:value="n">
        </div>
    
    <script type="text/javascript">
        Vue.config.productionTip = false
​
        //定义全局指令
        /* Vue.directive('fbind',{
            //指令与元素成功绑定时(一上来)
            bind(element,binding){
                element.value = binding.value
            },
            //指令所在元素被插入页面时
            inserted(element,binding){
                element.focus()
            },
            //指令所在的模板被重新解析时
            update(element,binding){
                element.value = binding.value
            }
        }) */
​
        new Vue({
            el:'#root',
            data:{
                name:'尚硅谷',
                n:1
            },
            directives:{
                //big函数何时会被调用?1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时。
                /* 'big-number'(element,binding){
                    // console.log('big')
                    element.innerText = binding.value * 10
                }, */
                big(element,binding){
                    console.log('big',this) //注意此处的this是window
                    // console.log('big')
                    element.innerText = binding.value * 10
                },
                fbind:{
                    //指令与元素成功绑定时(一上来)
                    bind(element,binding){
                        element.value = binding.value
                    },
                    //指令所在元素被插入页面时
                    inserted(element,binding){
                        element.focus()
                    },
                    //指令所在的模板被重新解析时
                    update(element,binding){
                        element.value = binding.value
                    }
                }
            }
        })
        
    </script>

生命周期(mounted(){})

1.又名:生命周期回调函数、生命周期函数、生命周期钩子。 2.是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。 3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。 4.生命周期函数中的this指向是vm 或 组件实例对象。

组件

components、extend

定义:实现应用中局部功能代码和资源的集合

使用组件的三大步骤

一、定义组件(创建组件)

使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别; 区别如下: 1.el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。 2.data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。 备注:使用template可以配置组件结构。

二、注册组件

1.局部注册:靠new Vue的时候传入components选项 2.全局注册:靠Vueponent('组件名',组件)

三、使用组件(写组件标签)

<school></school>

例如:

<!-- 准备好一个容器-->
        <div id="root">
            <hello></hello>
            <hr>
            <h1>{{msg}}</h1>
            <hr>
            <!-- 第三步:编写组件标签 -->
            <school></school>
            <hr>
            <!-- 第三步:编写组件标签 -->
            <student></student>
        </div>
​
        <div id="root2">
            <hello></hello>
        </div>
​
<script type="text/javascript">
        Vue.config.productionTip = false
​
        //第一步:创建school组件
        const school = Vue.extend({
            template:`
                <div class="demo">
                    <h2>学校名称:{{schoolName}}</h2>
                    <h2>学校地址:{{address}}</h2>
                    <button @click="showName">点我提示学校名</button>  
                </div>
            `,
            // el:'#root', //组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器。
            data(){
                return {
                    schoolName:'尚硅谷',
                    address:'北京昌平'
                }
            },
            methods: {
                showName(){
                    alert(this.schoolName)
                }
            },
        })
​
        //第一步:创建student组件
        const student = Vue.extend({
            template:`
                <div>
                    <h2>学生姓名:{{studentName}}</h2>
                    <h2>学生年龄:{{age}}</h2>
                </div>
            `,
            data(){
                return {
                    studentName:'张三',
                    age:18
                }
            }
        })
        
        //第一步:创建hello组件
        const hello = Vue.extend({
            template:`
                <div>   
                    <h2>你好啊!{{name}}</h2>
                </div>
            `,
            data(){
                return {
                    name:'Tom'
                }
            }
        })
        
        //第二步:全局注册组件
        Vueponent('hello',hello)
​
        //创建vm
        new Vue({
            el:'#root',
            data:{
                msg:'你好啊!'
            },
            //第二步:注册组件(局部注册)
            components:{
                school,
                student
            }
        })
​
        new Vue({
            el:'#root2',
        })
</script>

注意点:

1.关于组件名: 一个单词组成: 第一种写法(首字母小写):school 第二种写法(首字母大写):School 多个单词组成: 第一种写法(kebab-case命名):my-school 第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持) 备注: (1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。 (2).可以使用name配置项指定组件在开发者工具中呈现的名字。

2.关于组件标签: 第一种写法:<school></school> 第二种写法:<school/> 备注:不用使用脚手架时,<school/>会导致后续组件不能渲染。

3.一个简写方式: const school = Vue.extend(options) 可简写为:const school = options

//例如:
//定义组件
        const s = Vue.extend({
            name:'atguigu',
            template:`
                <div>
                    <h2>学校名称:{{name}}</h2>  
                    <h2>学校地址:{{address}}</h2>   
                </div>
            `,
            data(){
                return {
                    name:'尚硅谷',
                    address:'北京'
                }
            }
        })
//简写为
        //定义组件
        const s = {
            name:'atguigu',
            template:`
                <div>
                    <h2>学校名称:{{name}}</h2>  
                    <h2>学校地址:{{address}}</h2>   
                </div>
            `,
            data(){
                return {
                    name:'尚硅谷',
                    address:'北京'
                }
            }
        }

组件的嵌套

<!-- 准备好一个容器-->
        <div id="root">
            
        </div>
​
    <script type="text/javascript">
        Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
​
        //定义student组件
        const student = Vue.extend({
            name:'student',
            template:`
                <div>
                    <h2>学生姓名:{{name}}</h2>  
                    <h2>学生年龄:{{age}}</h2>   
                </div>
            `,
            data(){
                return {
                    name:'尚硅谷',
                    age:18
                }
            }
        })
        
        //定义school组件
        const school = Vue.extend({
            name:'school',
            template:`
                <div>
                    <h2>学校名称:{{name}}</h2>  
                    <h2>学校地址:{{address}}</h2>   
                    <student></student>
                </div>
            `,
            data(){
                return {
                    name:'尚硅谷',
                    address:'北京'
                }
            },
            //注册组件(局部)
            components:{
                student
            }
        })
​
        //定义hello组件
        const hello = Vue.extend({
            template:`<h1>{{msg}}</h1>`,
            data(){
                return {
                    msg:'欢迎来到尚硅谷学习!'
                }
            }
        })
        
        //定义app组件
        const app = Vue.extend({
            template:`
                <div>   
                    <hello></hello>
                    <school></school>
                </div>
            `,
            components:{
                school,
                hello
            }
        })
​
        //创建vm
        new Vue({
            template:'<app></app>',
            el:'#root',
            //注册组件(局部)
            components:{app}
        })
    </script>

VueComponent

1)school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。

2)我们只需要写<school/>或<school></school>,Vue解析时会帮我们创建school组件的实例对象, 即Vue帮我们执行的:new VueComponent(options)。

3)特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!

4)关于this指向: (1).组件配置中: data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。 (2).new Vue(options)配置中: data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。

5)VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。 Vue的实例对象,以后简称vm。

一个重要的内置关系(原型)

VueComponent.prototype.__proto__ === Vue.prototype

为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。

非单文件组件

一个文件中包含多个组件

单文件组件

一个文件中只包含一个组件

插件

因为vscode不认识.vue文件,所以要下载vetur插件

安装后快捷键<v自动补全标签

组成组件的解构

<template>
    <!--编写html结构 -->
</template>
<script>//编写js结构
    export default {//模块化       
    }
</script>
<style>/* 编写css结构 */</style>

文件组成

app.vue--服务index.html的组件,是所有组件的父组件

<template>
    <div>
        <School></School>
        <Student></Student>
    </div>
</template>
​
<script>
    //引入组件
    import School from './School.vue'
    import Student from './Student.vue'
​
    export default {
        name:'App',
        components:{
            School,
            Student
        }
    }
</script>
<style></style>

index.html--主页

<!-- 准备一个容器 -->
        <div id="root"></div>
        <script type="text/javascript" src="../js/vue.js"></script>
        <script type="text/javascript" src="./main.js"></script>

main.js--引入app,创建vue实例

import App from './App.vue'
​
new Vue({
    el:'#root',
    template:`<App></App>`,
    components:{App},
})

school、student--服务app.vue的子组件

<template>
    <div class="demo">
        <h2>学校名称:{{name}}</h2>
        <h2>学校地址:{{address}}</h2>
        <button @click="showName">点我提示学校名</button>  
    </div>
</template>
​
<script>
     export default {
        name:'School',
        data(){
            return {
                name:'尚硅谷',
                address:'北京昌平'
            }
        },
        methods: {
            showName(){
                alert(this.name)
            }
        },
    }
</script>
​
<style>
    .demo{
        background-color: orange;
    }
</style>

Vue脚手架(Vue CLI)

Vue脚手架就是Vue官方提供的标准化开发工具(开发平台)

.vue文件如果没有脚手架解析,浏览器解析不了

网站:Vue官网--生态系统--Vue CLI

安装步骤

1、仅第一次执行:

全局安装@Vue/cli

npm install -g @vue/cli(在cmd中输入)

测试:在cmd中输入vue

2、切换到你要创建的目录,然后使用命令创建项目

vue create xxxx

xxx为项目名

3、启动项目

vue run serve

备注:

如出现下载缓慢请配置npm淘宝镜像:npm config set registry https://registry.npm.taobao(在cmd中输入)

Vue脚手架隐藏了所有Webpack相关的配置,若想查看具体的webpack配置,执行:vue inspect> output.js

可更改的参数

生态系统--Vue CLI--配置参考

文件

components:存放子组件

src:存放app.vue和main.js

assets:存放静态文件(图片、视频之类)

public:存放html文件和ico图标

关于不同版本的Vue:
    
        1.vue.js与vue.runtime.xxx.js的区别:
                (1).vue.js是完整版的Vue,包含:核心功能+模板解析器。
                (2).vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
​
        2.因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用
            render函数接收到的createElement函数去指定具体内容。

$ref属性

  1. 被用来给元素或子组件注册引用信息(id的替代者)

  2. 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)

  3. 使用方式:

(1)打标识:<h1 ref="xxx">.....</h1><School ref="xxx"></School>

(2)获取:this.$refs.xxx

<template>
    <div>
        <h1 v-text="msg" ref="title"></h1>
        <button ref="btn" @click="showDOM">点我输出上方的DOM元素</button>
        <School ref="sch"/>
    </div>
</template>
​
<script>
    //引入School组件
    import School from './components/School'
​
    export default {
        name:'App',
        components:{School},
        data() {
            return {
                msg:'欢迎学习Vue!'
            }
        },
        methods: {
            showDOM(){
                console.log(this.$refs.title) //真实DOM元素
                console.log(this.$refs.btn) //真实DOM元素
                console.log(this.$refs.sch) //School组件的实例对象(vc)
            }
        },
    }
</script>
​

props配置

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

\2. 传递数据:<Demo name="xxx"/>

\3. 接收数据:

\1. 第一种方式(只接收):props:['name']

\2. 第二种方式(限制类型):props:{name:String}

\3. 第三种方式(限制类型、限制必要性、指定默认值):

   props:{
​
•     name:{
​
•     type:String, *//类型*
​
•     required:true, *//必要性*
​
•     *default*:'老王' *//默认值*
​
•     }
​
•    }

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

//App文件
<template>
    <div>
        <Student name="李四" sex="女" :age="18"/>
    </div>
</template>
​
<script>
    import Student from './components/Student'
​
    export default {
        name:'App',
        components:{Student}
    }
</script>
​
//student文件
<template>
    <div>
        <h1>{{msg}}</h1>
        <h2>学生姓名:{{name}}</h2>
        <h2>学生性别:{{sex}}</h2>
        <h2>学生年龄:{{myAge+1}}</h2>
        <button @click="updateAge">尝试修改收到的年龄</button>
    </div>
</template>
​
<script>
    export default {
        name:'Student',
        data() {
            console.log(this)
            return {
                msg:'我是一个尚硅谷的学生',
                myAge:this.age
            }
        },
        methods: {
            updateAge(){
                this.myAge++
            }
        },
        //简单声明接收
        // props:['name','age','sex'] 
​
        //接收的同时对数据进行类型限制
        /* props:{
            name:String,
            age:Number,
            sex:String
        } */
​
        //接收的同时对数据:进行类型限制+默认值的指定+必要性的限制
        props:{
            name:{
                type:String, //name的类型是字符串
                required:true, //name是必要的
            },
            age:{
                type:Number,
                default:99 //默认值
            },
            sex:{
                type:String,
                required:true
            }
        }
    }
</script>

mixin混入(合)

\1. 功能:可以把多个组件共用的配置提取成一个混入对象

\2. 使用方式:

第一步定义混合:

    { data(){....},  
​
•       methods:{....}, 
​
•       mixin:{}   ....  }

第二步使用混入:

全局混入:Vue.mixin(xxx)

局部混入:mixins:['xxx']

插件

\1. 功能:用于增强Vue

\2. 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。

\3. 定义插件:

  对象.install = function (Vue, options) {
​
•    *// 1. 添加全局过滤器*
​
•    Vue.filter(....)
​
  
​
•    *// 2. 添加全局指令*
​
•    Vue.directive(....)
​
  
​
•    *// 3. 配置全局混入(合)*
​
•    Vue.mixin(....)
​
  
​
•    *// 4. 添加实例方法*
​
•    Vue.prototype.$myMethod = function () {...}
​
•    Vue.prototype.$myProperty = xxxx
​
  }

\4. 使用插件:Vue.use()()中填写插件所在的文件名

scoped样式

\1. 作用:让样式在局部生效,防止冲突。

\2. 写法:<style scoped>

不同的组件写的css汇总的时候会冲突,如果类名写的一样会遵循最后引入的

<style lang=""lang的值可以写less,意为用less编写样式

组件自定义事件

\1. 一种组件间通信的方式,适用于:子组件 ===> 父组件

\2. 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。

\3. 绑定自定义事件:

(1)第一种方式,在父组件中:<Demo @atguigu="test"/><Demo v-on:atguigu="test"/>

(2)第二种方式,在父组件中:

 <Demo ref="demo"/>
​
•    ......
​
•    mounted(){
​
•      this.$refs.xxx.$on('atguigu',this.test)
​
•    }

(3)若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。

\4. 触发自定义事件:this.$emit('atguigu',数据)

\5. 解绑自定义事件this.$off('atguigu')

\6. 组件上也可以绑定原生DOM事件,需要使用native修饰符。

\7. 注意:通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!

全局事件总线

\1. 一种组件间通信的方式,适用于任意组件间通信。

\2. 安装全局事件总线:

 new Vue({
​
   ......
​
   beforeCreate() {
​
•     Vue.prototype.$bus = this *//安装全局事件总线,$bus就是当前应用的vm*
​
   },
​
  ......
​
  }) 

\3. 使用事件总线:

(1) 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。

 methods(){
​
•    demo(data){......}
​
   }
​
   ......
​
   mounted() {
​
•    this.$bus.$on('xxxx',this.demo)
​
   }

(2)提供数据:this.$bus.$emit('xxxx',数据)

\4. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。

消息订阅与发布

\1. 一种组件间通信的方式,适用于任意组件间通信。

\2. 使用步骤:

(1) 安装pubsub:npm i pubsub-js

(2)引入: import pubsub from 'pubsub-js'

\3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。

   methods(){
​
•    demo(data){......}
​
   }
​
   ......
​
   mounted() {
​
•    this.pid = pubsub.subscribe('xxx',this.demo) *//订阅消息*
​
   }

\4. 提供数据:pubsub.publish('xxx',数据)

\5. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去取消订阅。

nextTick

\1. 语法:this.$nextTick(回调函数)

\2. 作用:在下一次 DOM 更新结束后执行其指定的回调。

\3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

动画与过度

\1. 作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。

\2. 图示:

\3. 写法:

1、准备好样式:

- 元素进入的样式:

(1). v-enter:进入的起点

(2). v-enter-active:进入过程中

(3). v-enter-to:进入的终点

- 元素离开的样式:

(1). v-leave:离开的起点

(2). v-leave-active:离开过程中

(3). v-leave-to:离开的终点

2、使用<transition>包裹要过度的元素,并配置name属性:

<transition name="hello">
​
    <h1 v-show="isShow">你好啊!</h1>
​
</transition>

\3. 备注:若有多个元素需要过度,则需要使用:<transition-group>,且每个元素都要指定key值。

//动画
<template>
    <div>
        <button @click="isShow = !isShow">显示/隐藏</button>
        <transition name="hello" appear>
            <h1 v-show="isShow">你好啊!</h1>
        </transition>
    </div>
</template>
​
<script>
    export default {
        name:'Test',
        data() {
            return {
                isShow:true
            }
        },
    }
</script>
​
<style scoped>
    h1{
        background-color: orange;
    }
​
    .hello-enter-active{
        animation: atguigu 0.5s linear;
    }
​
    .hello-leave-active{
        animation: atguigu 0.5s linear reverse;
    }
​
    @keyframes atguigu {
        from{
            transform: translateX(-100%);
        }
        to{
            transform: translateX(0px);
        }
    }
</style>
//过度
<template>
    <div>
        <button @click="isShow = !isShow">显示/隐藏</button>
        <transition-group name="hello" appear>
            <h1 v-show="!isShow" key="1">你好啊!</h1>
            <h1 v-show="isShow" key="2">尚硅谷!</h1>
        </transition-group>
    </div>
</template>
​
<script>
    export default {
        name:'Test',
        data() {
            return {
                isShow:true
            }
        },
    }
</script>
​
<style scoped>
    h1{
        background-color: orange;
    }
    /* 进入的起点、离开的终点 */
    .hello-enter,.hello-leave-to{
        transform: translateX(-100%);
    }
    .hello-enter-active,.hello-leave-active{
        transition: 0.5s linear;
    }
    /* 进入的终点、离开的起点 */
    .hello-enter-to,.hello-leave{
        transform: translateX(0);
    }
​
</style>

配置代理

### 方法一

在vue.config.js中添加如下配置:

devServer:{
​
 proxy:"http://localhost:5000"
​
}

说明:

\1. 优点:配置简单,请求资源时直接发给前端(8080)即可。

\2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。

\3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)

### 方法二

编写vue.config.js配置具体代理规则:

module.exports = {
​
  devServer: {
​
   proxy: {
​
   '/api1': {*// 匹配所有以 '/api1'开头的请求路径*
​
•    target: 'http://localhost:5000',*// 代理目标的基础路径*
​
•    changeOrigin: true,
​
•    pathRewrite: {'^/api1': ''}
​
   },
​
   '/api2': {*// 匹配所有以 '/api2'开头的请求路径*
​
•    target: 'http://localhost:5001',*// 代理目标的基础路径*
​
•    changeOrigin: true,
​
•    pathRewrite: {'^/api2': ''}
​
   }
​
  }
​
 }
​
}
//  *changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000*
​
//  *changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080*
​
//  *changeOrigin默认值为true*

说明:

\1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。

\2. 缺点:配置略微繁琐,请求资源时必须加前缀。

插槽slot

\1. 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件

\2. 分类:默认插槽、具名插槽、作用域插槽

\3. 使用方式:

(1). 默认插槽:

   父组件中:
​
•       <Category>
​
                 <div>html结构1</div>
​
•       </Category>
​
   子组件中:
​
•       <template>
​
                  <div>
​
<!-- 定义插槽:父组件中div的内容会在插槽中展示;若父组件中没有结构,则会展示插槽中的默认内容-->
​
•           <slot>插槽默认内容...</slot>
​
•         </div>
​
•       </template>

(2). 具名插槽:

​
​
   父组件中:
​
•       <Category>
​
•         <template slot="center">
​
                    <div>html结构1</div>
​
•         </template>
​
   
​
•         <template v-slot:footer>
​
                     <div>html结构2</div>
​
•         </template>
​
•       </Category>
​
   子组件中:
​
•       <template>
​
                  <div>
​
•           *<!-- 定义插槽 -->*
​
•           <slot name="center">插槽默认内容...</slot>
​
•           <slot name="footer">插槽默认内容...</slot>
​
•         </div>
​
•       </template>

1、父组件中的slot=“center”与子组件中的name=“center”对应(即name为center的内容会在slot为center的地方展示)

2、两种写法:slot=“xxx”、v-slot:xxx;第二种写法只能应用于template标签上

(3). 作用域插槽:

\1. 理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)

\2. 具体编码:

    父组件中:
​
•        <Category>
​
•         <template scope="scopeData">
​
•           *<!-- 生成的是ul列表 -->*
​
•           <ul>
​
•            <li v-for="g in scopeData.games" :key="g">{{g}}</li>
​
•           </ul>
​
•         </template>
​
•        </Category>
​
•     
​
•        <Category>
​
•         <template slot-scope="scopeData">
​
•           *<!-- 生成的是h4标题 -->*
​
•           <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4>
​
•         </template>
​
•        </Category>
​
•     子组件中:
​
•         <template>
​
                     <div>
​
•             <slot :games="games"></slot>
​
•           </div>
​
•         </template>
​
•        
​
                 <script>
​
•           export default {
​
•             name:'Category',
​
•             props:['title'],
​
•             //数据在子组件自身
​
•             data() {
​
•               return {
​
•                 games:['红色警戒','穿越火线','劲舞团','超级玛丽']
​
•               }
​
•             },
​
•           }
​
•         </script>

1、父给子传结构、子给父传数据。

2、子组件中::games="games"意为给父组件中传入data中games数组。

3、父组件中scope="scopeData"必须写在template标签中,意为接收子组件传过来的数据,打印scopeData为{games:['红色警戒','穿越火线','劲舞团','超级玛丽']}。scope的值可以任意命名

4、子组件可以传递多个数据,父组件中只写一次scope就可以接收到多个数据

5、解构赋值:scope="scopeData"也可以写为scope="{games}"

6、两种写法:scope="scopeData"、slot-scope="scopeData" 

Vuex

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

安装:

Vue2只能用vuex3

vue3只能用vuex4

npm i vuex@3 @3:指定版本

引入:import Vuex from 'vuex'

使用插件:Vue.use(Vuex)

搭建环境

\1. 创建文件:src/store/index.js

  *//引入Vue核心库*
​
  *import* Vue *from* 'vue'
​
  *//引入Vuex*
​
  *import* Vuex *from* 'vuex'
​
  *//应用Vuex插件*
​
  Vue.use(Vuex)
​
  *//准备actions对象——响应组件中用户的动作*
​
  const actions = {}
​
  *//准备mutations对象——修改state中的数据*
​
  const mutations = {}
​
  *//准备state对象——保存具体的数据*
​
  const state = {} 
​
  *//创建并暴露store*
​
  *export* *default* new Vuex.Store({
​
   actions,
​
   mutations,
​
   state
​
  })

\2. 在main.js中创建vm时传入store配置项

  *//引入store*
​
  *import* store *from* './store'
​
  ......
  
​
  *//创建vm*
​
  new Vue({
​
   el:'#app',
​
   render: h => h(App),
​
   store
​
  })

基本使用

\1. 初始化数据、配置actions、配置mutations,操作文件store.js

 *//引入Vue核心库*
​
  *import* Vue *from* 'vue'
​
  *//引入Vuex*
​
  *import* Vuex *from* 'vuex'
​
  *//引用Vuex*
​
  Vue.use(Vuex)  
​
  const actions = {
​
•    *//响应组件中加的动作*
​
   jia(context,value){
​
•     *// console.log('actions中的jia被调用了',miniStore,value)*
​
•     contextmit('JIA',value)
​
   },
​
  }
  
  const mutations = {
​
•    *//执行加*
​
   JIA(state,value){
​
•     *// console.log('mutations中的JIA被调用了',state,value)*
​
•     state.sum += value
​
   }
​
  }  
​
  *//初始化数据*
​
  const state = {
​
   sum:0
​
  }  
​
  *//创建并暴露store*
​
  *export* *default* new Vuex.Store({
​
   actions,
​
   mutations,
​
   state,
​
  })

\2. 组件中读取vuex中的数据:$store.state.sum

\3. 组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)$storemit('mutations中的方法名',数据)

> 备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写**dispatch**,直接编写****commit**

getters

\1. 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。

\2. 在store.js中追加getters配置

const getters = {
​
   bigSum(state){
​
     return state.sum * 10
​
   }
​
  }
  
​
  *//创建并暴露store*
​
  export default new Vuex.Store({
   ......
   getters
​
  })

\3. 组件中读取数据:$store.getters.bigSum

四个map方法的使用

\1. mapState方法:用于帮助我们映射state中的数据为计算属性

//靠程序员自己亲自去写计算属性
            computed:{
            //靠程序员自己亲自去写计算属性
             sum(){
                return this.$store.state.sum
            },
            school(){
                return this.$store.state.school
            },
            subject(){
                return this.$store.state.subject
            }, 
                
computed: {
​
•    *//借助mapState生成计算属性:sum、school、subject(对象写法)*
​
•    ...mapState({sum:'sum',school:'school',subject:'subject'}),
​
•      
​
•    *//借助mapState生成计算属性:sum、school、subject(数组写法)*
​
•    ...mapState(['sum','school','subject']),
​
  }

\2. mapGetters方法:用于帮助我们映射getters中的数据为计算属性

computed:{
    bigSum(){
                return this.$store.getters.bigSum
            }
}
​
​
computed: {
​
•    *//借助mapGetters生成计算属性:bigSum(对象写法)*
​
•    ...mapGetters({bigSum:'bigSum'}),
​
  
​
•    *//借助mapGetters生成计算属性:bigSum(数组写法)*
​
•    ...mapGetters(['bigSum'])
​
  },

\3. mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

methods:{
    //程序员亲自写方法
             incrementOdd(){
                this.$store.dispatch('jiaOdd',this.n)
            },
            incrementWait(){
                this.$store.dispatch('jiaWait',this.n)
            }, 
}
​
methods:{
​
•    *//靠mapActions生成:incrementOdd、incrementWait(对象形式)*
​
•    ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
​
  
​
•    *//靠mapActions生成:incrementOdd、incrementWait(数组形式)*
​
•    ...mapActions(['jiaOdd','jiaWait'])
​
  }

\4. mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$storemit(xxx)的函数

methods:{
    //程序员亲自写方法
             increment(){
                this.$storemit('JIA',this.n)
            },
            decrement(){
                this.$storemit('JIAN',this.n)
            }, 
}
methods:{
​
•    *//靠mapActions生成:increment、decrement(对象形式)*
​
•    ...mapMutations({increment:'JIA',decrement:'JIAN'}),
​
•    
​
•    *//靠mapMutations生成:JIA、JIAN(对象形式)*
​
•    ...mapMutations(['JIA','JIAN']),
​
  }

> 备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。

vuex模块化+命名空间

\1. 目的:让代码更好维护,让多种数据分类更加明确。

\2. 修改store.js

  const countAbout = {
​
   namespaced:true,*//开启命名空间*
​
   state:{x:1},
​
   mutations: { ... },
​
   actions: { ... },
​
   getters: {
​
    bigSum(state){
​
    return state.sum * 10
    }
   }
  }  
  const personAbout = {
​
   namespaced:true,*//开启命名空间*
​
   state:{ ... },
​
   mutations: { ... },
​
   actions: { ... }
​
  }
​
  
​
  const store = new Vuex.Store({
​
   modules: {
​
•    countAbout,
​
•    personAbout
​
   }
​
  })

\3. 开启命名空间后,组件中读取state数据:

  *//方式一:自己直接读取*
​
  this.$store.state.personAbout.list
​
  *//方式二:借助mapState读取:*
​
  ...mapState('countAbout',['sum','school','subject']),

\4. 开启命名空间后,组件中读取getters数据:

  *//方式一:自己直接读取*
​
  this.$store.getters['personAbout/firstPersonName']
​
  *//方式二:借助mapGetters读取:*
​
  ...mapGetters('countAbout',['bigSum'])

\5. 开启命名空间后,组件中调用dispatch

  *//方式一:自己直接dispatch*
​
  this.$store.dispatch('personAbout/addPersonWang',person)
​
  *//方式二:借助mapActions:*
​
  ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})

\6. 开启命名空间后,组件中调用commit

  *//方式一:自己直接commit*
​
  this.$storemit('personAbout/ADD_PERSON',person)
​
  *//方式二:借助mapMutations:*
​
  ...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),

路由

\1. 理解: 一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。

\2. 前端路由:key是路径,value是组件。

基本使用

\1. 安装vue-router,命令:npm i vue-router

vue-router4只能在vue3中使用

vue-router3只能在vue2中使用

\2. 应用插件:Vue.use(VueRouter)

\3. 编写router配置项:

  *//引入VueRouter*
​
  *import* VueRouter *from* 'vue-router'
​
  *//引入Luyou 组件*
​
  *import* About *from* '../components/About'
​
  *import* Home *from* '../components/Home'
​
​
  *//创建router实例对象,去管理一组一组的路由规则*
​
  const router = new VueRouter({
​
   routes:[
​
•     {
​
•      path:'/about',
​
•      component:About
​
•     },
​
•     {
​
•      path:'/home',
​
•      component:Home
​
•     }
​
   ]
​
  })
​
  
​
  *//暴露router*
​
  *export* *default* router

\4. 实现切换(active-class可配置高亮样式)

  <router-link active-class="active" to="/about">About</router-link>

\5. 指定展示位置

  <router-view></router-view>

几个注意点

\1. 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。

\2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。

\3. 每个组件都有自己的$route属性,里面存储着自己的路由信息。

\4. 整个应用只有一个router,可以通过组件的$router属性获取到。

多级路由(多级路由)

\1. 配置路由规则,使用children配置项:

    routes:[
​
   {
​
•     path:'/about',
​
•     component:About,
​
   },
​
   {
​
•     path:'/home',
​
•     component:Home,
​
•     children:[ *//通过children配置子级路由*
​
•      {
​
•        path:'news', *//此处一定不要写:/news*
​
•        component:News
​
•      },
​
•      {
​
•        path:'message',*//此处一定不要写:/message*
​
•        component:Message
​
•      }
​
•     ]
​
   }
​
  ]

\2. 跳转(要写完整路径):

  <router-link to="/home/news">News</router-link>

路由的query参数

\1. 传递参数

  *<!-- 跳转并携带query参数,to的字符串写法 -->*
​
  <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
​
•        
​
  *<!-- 跳转并携带query参数,to的对象写法 -->*
​
  <router-link 
​
   :to="{
​
•     path:'/home/message/detail',
​
•     query:{
​
•      id:666,
​
•        title:'你好'
​
•     }
​
   }"
​
  >跳转</router-link>

\2. 接收参数:

  $route.query.id
​
  $route.query.title

命名路由

\1. 作用:可以简化路由的跳转。

\2. 如何使用

(1). 给路由命名:

   {
​
•     path:'/demo',
​
•     component:Demo,
​
•     children:[
​
•      {
​
•        path:'test',
​
•        component:Test,
​
•        children:[
​
•         {
​
•              name:'hello' *//给路由命名*
​
•           path:'welcome',
​
•           component:Hello,
​
•         }
​
•        ]
​
•      }
​
•     ]
​
   }

(2). 简化跳转:

  *<!--简化前,需要写完整的路径 -->*
​
   <router-link to="/demo/test/welcome">跳转</router-link>
​
   
​
   *<!--简化后,直接通过名字跳转 -->*
​
   <router-link :to="{name:'hello'}">跳转</router-link>
​
   
​
   *<!--简化写法配合传递参数 -->*
​
   <router-link 
​
•     :to="{
​
•      name:'hello',
​
•      query:{
​
•        id:666,
​
•         title:'你好'
​
•      }
​
•     }"
​
   >跳转</router-link>

路由的params参数

\1. 配置路由,声明接收params参数

  {
​
   path:'/home',
​
   component:Home,
​
   children:[
​
•     {
​
•      path:'news',
​
•      component:News
​
•     },
​
•     {
​
•      component:Message,
​
•      children:[
​
•        {
​
•         name:'xiangqing',
​
•         path:'detail/:id/:title', *//使用占位符声明接收params参数*
​
•         component:Detail
​
•        }
​
•      ]
​
•     }
​
   ]
​
  }

\2. 传递参数

 *<!-- 跳转并携带params参数,to的字符串写法 -->*
​
  <router-link :to="/home/message/detail/666/你好">跳转</router-link>
​
•        
​
  *<!-- 跳转并携带params参数,to的对象写法 -->*
​
  <router-link 
​
   :to="{
​
•     name:'xiangqing',
​
•     params:{
​
•      id:666,
​
•        title:'你好'
​
•     }
​
   }"
​
  >跳转</router-link>

> 特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

\3.接收参数:

  $route.params.id
​
  $route.params.title

路由的props配置

作用:让路由组件更方便的收到参数

{
​
  name:'xiangqing',
​
  path:'detail/:id',
​
  component:Detail,
​
​
​
  *//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件*
​
  *// props:{a:900}*
​
​
​
  *//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件*
​
  *// props:true*
​
  
​
  *//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件*
​
  props(route){
​
   *return* {
​
•     id:route.query.id,
​
•     title:route.query.title
​
   }
​
  }
​
}

router-link的replace属性

\1. 作用:控制路由跳转时操作浏览器历史记录的模式

\2. 浏览器的历史记录有两种写入方式:分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push

\3. 如何开启replace模式:<router-link replace .......>News</router-link>

编程式路由导航

\1. 作用:不借助<router-link>实现路由跳转,让路由跳转更加灵活

区别:声明式导航能做的,编程式导航都能做。而且除了可以进行路由跳转,还可以做一些其他的业务逻辑(比如:注册页面点击注册完成后,要先提交数据再跳转)

\2. 具体编码:

  *//$router的两个API*
​
  this.$router.push({
​
   name:'xiangqing',
​
•     params:{
​
•      id:xxx,
​
•      title:xxx
​
•     }
​
  })
​
  
​
  this.$router.replace({
​
   name:'xiangqing',
​
•     params:{
​
•      id:xxx,
​
•      title:xxx
​
•     }
​
  })
​
  this.$router.forward() *//前进*
​
  this.$router.back() *//后退*
​
  this.$router.go() *//可前进也可后退*

缓存路由组件

\1. 作用:让不展示的路由组件保持挂载,不被销毁。

\2. 具体编码:

<template>
<!--缓存一个路由组件 -->
    <keep-alive include="News"> 
        <router-view></router-view>
    </keep-alive>
<!--缓存多个路由组件 -->
    <keep-alive :include="['News','Message']"> 
        <router-view></router-view>
    </keep-alive>
</template>

注意:include中写组件名。如果不写include,则表示全部缓存

两个新的生命周期钩子

\1. 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。

\2. 具体名字:

\1. activated路由组件被激活时触发。

\2. deactivated路由组件失活时触发。

路由守卫

\1. 作用:对路由进行权限控制

\2. 分类:全局守卫、独享守卫、组件内守卫

\3. 全局守卫:

  *//全局前置守卫:初始化时执行、每次路由切换前执行*
​
  router.beforeEach((to,from,next)=>{
​
   console.log('beforeEach',to,from)
​
   *if*(to.meta.isAuth){ *//判断当前路由是否需要进行权限控制*
​
•     *if*(localStorage.getItem('school') === 'atguigu'){ *//权限控制的具体规则*
​
•      next() *//放行*
​
•     }*else*{
​
•      alert('暂无权限查看')
​
•      *// next({name:'guanyu'})*
​
•     }
​
   }*else*{
​
•     next() *//放行*
​
   }
​
  })
​
  
  *//全局后置守卫:初始化时执行、每次路由切换后执行*
​
  router.afterEach((to,from)=>{
​
   console.log('afterEach',to,from)
​
   *if*(to.meta.title){ 
​
•     document.title = to.meta.title *//修改网页的title*
​
   }*else*{
​
•     document.title = 'vue_test'
​
   }
​
  })

\4. 独享守卫:

  beforeEnter(to,from,next){
​
   console.log('beforeEnter',to,from)
​
   *if*(to.meta.isAuth){ *//判断当前路由是否需要进行权限控制*
​
•     *if*(localStorage.getItem('school') === 'atguigu'){
​
•      next()
​
•     }*else*{
​
•      alert('暂无权限查看')
​
•      *// next({name:'guanyu'})*
​
•     }
​
   }*else*{
​
•     next()
​
   }
​
  }

\5. 组件内守卫:

  *//进入守卫:通过路由规则,进入该组件时被调用*
​
  beforeRouteEnter (to, from, next) {
​
  },
​
  *//离开守卫:通过路由规则,离开该组件时被调用*
​
  beforeRouteLeave (to, from, next) {
​
  }

路由器的两种工作模式

\1. 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。

\2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。

\3. hash模式:

(1). 地址中永远带着#号,不美观 。

(2). 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。

(3). 兼容性较好。

\4. history模式:

(1). 地址干净,美观 。

(2). 兼容性和hash模式相比略差。

(3). 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。

UI组件库

移动端常用UI组件库

  • Vant:Vant 3 - Mobile UI Components built on Vue

  • Cube UI:cube-ui Document

  • Mint UI:Mint UI

PC端常用UI组件库

  • Element UI:Element - The world's most popular Vue UI framework

  • IView UI:iView - A high quality UI Toolkit based on Vue.js

京东设计的UI库:NutUI - 移动端 Vue2、Vue3、小程序 组件库

按需引入:如果全部引入组件库会导致文件太大,所以需要按需引入(组件库官网中会有提示)

更多推荐

Vue2---