Vue2学习记录

时间:2024-11-28 07:02:42

前言

这篇笔记,是根据B站尚硅谷的Vue2网课学习整理的,用来学习的

如果有错误,还请大佬指正

Vue核心

Vue简介

Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架

它基于标准 HTML、CSS 和 JavaScript 构建,

并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面


官网:Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org)

初识Vue

  • Vue进行工作,需要提供 Vue实例 和 模版
  • 通过 new Vue() 创建出Vue实例对象
  • 在Vue实例对象中,需要绑定模板
    <!--准备容器-->
    <div id="test01">
        Hello {{word}}!!
    </div>
    <script>
        //创建Vue实例
        new Vue({
            el: '#test01',//指定当前实例为那个模版服务
            data: {
                word: 'world'
            }//提供数据,供指定模版使用
        })
    </script>

 MVVM模型

Vue 仿照MVVM来进行设计

分为模型(Model),视图(View)和视图模型(ViewModel)三部分

  • M:模型(Model) -> data中的数据
  • V:视图(View) -> 模版,即 DOM
  • VM:视图模型(ViewModel) -> Vue 实例对象

模版绑定 与 data 写法

模版绑定

方法一:

使用 el 来绑定

语法 : el : ' css选择器 '

        new Vue({
            el: '#test01',
            data: {
                word: 'world'
            }
        })

方法二:

语法:实例对象 . $mount ( ' css选择器 ' )

        const vm = new Vue({
            data: {
                word: 'world!!!'
            }
        })
        vm.$mount('.test01')

data写法

写法一:对象式

语法:data : { 数据对象 }

            data: {
                word: 'world!!!'
            }

写法二:函数式

必须返回一个数据对象

不要写箭头函数

            data: function(){
                return{
                    word:'world!!!'
                }
            }

模版语法

Vue 使用一种基于 HTML 的模板语法,

使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上

简单来说:

        将Vue实例中的数据,呈现到 HTML 上

插值语法

可以分析标签体内容(标签包裹内容)

语法:

        {{JS表达式}}

示例:

    <div id="test01">
        Hello {{word}}
        <!-- {{}} 中的内容会被替换为Vue实例中的数据-->
    </div>
    
    <script>
        new Vue({
            el:'#test01',
            data:{
                word:'world!!!'
            }
        })
    </script>

{{ }} 中的内容会被替换为Vue实例中的数据

指令语法

可以解析标签(标签属性、标签体内容),并可以绑定事件

指令有很多,以 v- 为开头

例:v-bind:  (可简写为 : )

        v-bind : 标签属性 = 'JS表达式'   或   : 标签属性 = 'JS表达式'

    <div id="test02">
        <a v-bind:href="Prohibited">未满18不要打开</a>
        <!-- "" 中的内容会被替换为Vue实例中的数据-->
    </div>

    <script>
        new Vue({
            el: '#test02',
            data: {
                Prohibited: 'https://store.steampowered.com/app/2358720/_/'
            }
        })
    </script>

数据绑定

单向数据绑定

语法:

v-bind : 标签属性 = 'JS表达式'   或简写为   : 标签属性 = 'JS表达式'

数据只能从data流向页面

即 修改data中的数据,同时改变页面数据

双向数据绑定

语法:

v-model:value="JS表达式" 或简写为 v-model="JS表达式"

用于有value的标签

数据不仅能从 data 流向页面,还能从页面流向data

即 修改data中的数据,同时改变页面数据 ; 修改页面中的数据,同时改变data数据

数据代理

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

Object . defineProperty()

Object . defineProperty()可以为对象添加属性

语法:

Object . defineProperty ( 对象名 , ' 添加属性 ' , {

        get函数 ,

        set函数

})

get函数需要有返回值,作为添加属性的值

当读取所添加属性时,get函数会被调用

当修改所添加属性时,set函数会被调用

        let num = 18
        let Person = {
            name: 'zhao',
            school: 'sn'
        }
        Object.defineProperty(Person, 'age', {
            //读取age时,调用get函数,返回值为age的值
            get() {
                return num
            },
            //修改age时,调用set函数,修改值作为参数
            set(value) {
                num = value//将参数,也就是修改值,传给num
            }
        })

使用Object . defineProperty(),实现了Person对num的数据代理,

修改Person的属性值,就可以修改num

Vue中的数据代理

在Vue实例对象中,data的数据存放在Vue._data中

为了更方便地操作data中的数据,Vue通过Object . defineProperty(),

对data中数据进行了数据代理

事件处理 

事件基本使用

使用指令 v-on 来进行事件绑定

语法:v-on : 事件 = " 函数名 "

        简写:@事件 = " 函数名 "

        对应函数需要配置到Vue实例里,methods对象中

    <div>
        <button v-on:click="test01">普通按钮01</button>
    </div>
    <script>
        const vm=new Vue({
            el:'div',
            methods:{
                test01(){
                    console.log(11111)                    
                }
            }
        })
    </script>

函数参数

    <div>
        <button v-on:click="test02">普通按钮02</button>
        <button v-on:click="test03(3)">普通按钮03</button>
        <button v-on:click="test04($event,4,44)">普通按钮04</button>
    </div>
    <script>
        const vm=new Vue({
            el:'div',
            methods:{
                test02(e){
                    console.log(e)
                    console.log(2222)                    
                },
                test03(a){
                    console.log(a)
                    console.log(3333)                    
                },
                test04(e,a,b){
                    console.log(e,a,b)
                    console.log(4444)
                }
            }
        })
    </script>

 以上代码为例:

  1. 没有参数时,传入事件对象(test02)
  2. 有参数时,没有事件对象(test03)
  3. 可以用$enevt,来配置事件对象(test04)

注:

  • 不要用箭头函数,因为this将不再指向vm

事件修饰符

用于修饰事件

语法:v-on : 事件 . 事件修饰符 = " 函数名 "

        简写:@事件 . 事件修饰符 = " 函数名 "

事件修饰符 作用
prevent 阻止事件的默认行为
stop 停止事件冒泡
once 事件仅触发一次
capture 使用事件捕获
self e.target为当前元素时触发
passive 默认行为立即执行,再进行回调函数

示例:

    <div class="out" @click="work">
        out
        <div class="in" @click.stop="work">
            <!--阻止事件冒泡-->
            in
        </div>
    </div>

修饰符可以连写

        <div class="in" @click.stop.prevent="work" >

按键修饰符

Vue提供了一些触发键盘事件时使用的按键修饰符

用于在按下某个按键时,触发事件

语法:v-on : 键盘事件 . 按键别名 = " 函数名 "

        简写:@键盘事件 . 按键别名 = " 函数名 "

按键别名为Vue提供,即 按键码的别名

    <!--在按下回车键时触发事件-->
    <input type="text" @keyup.enter="work">

一些常用键盘别名

  • 回车键 ==》enter
  • 删除键 ==》delete
  • 退出键 ==》esc
  • 空格键 ==》space
  • 换行键 ==》tab(最好与keyup使用)
  • 上键 ==》up
  • 下键 ==》down
  • 左键 ==》left
  • 右键 ==》right

未提供的键盘别名可以用按键Key值,使用短横线命名法

系统修饰键

如ctrl,alt,shift,meta(win键)有特殊用法

配合keyup使用:

        按下系统修饰键,再按下并放开其他键时,触发事件

配合keydown使用:

        按下系统修饰键即触发事件

定制按键别名

语法:

        Vue.config.keyCodes.自定义按键名= 键码

计算属性

概念

属性:Vue实例中,data中的都认定为属性

计算属性:所需要的属性不存在,需要通过已有属性来计算获得

计算属性需要配置到Vue实例里,computed对象中

语法:

        const vm=new Vue({
            el:'.root',
            data:{
                    属性
                },
            computed:{
                计算属性名:{
                    get函数
                }
            }
        })

通过get()函数来进行计算,需要有return返回值,作为计算属性的值

get(调用时机):

  • 在初次读取计算属性时,get()调用
  • 计算属性所依赖属性数据发生变化时,也会调用

备注:

  • 也可以调用set()函数,进行数据改动
  • get()和set()函数的this均指向Vue实例
  • 计算属性会最终会出现在Vue实例上,直接调用即可

案例:

    <div class="root">
        <input type="text" v-model:value="xing"><br>
        <input type="text" v-model:value="ming"><br>
        <p>
            全名:{{quanming}}
        </p>
    </div>
    <script>
        const vm = new Vue({
            el: '.root',
            data: {
                xing: '张',
                ming: '三'
            },
            computed: {
                quanming: {
                    get() {
                        return this.xing + this.ming
                    }
                }
            }
        })
    </script>

简写

在没有写set()函数时,可以使用简写

语法:

            computed: {
                计算属性名() {
                    函数体
                }
            }
            computed: {
                quanming() {
                    return this.xing + this.ming
                }
            }

监视属性

概念与语法

可以设置被监视的属性,当被监视属性发生改变时,会自行调用函数

写法1:

        const vm = new Vue({
            el: 'div',
            watch: {
                监视属性名: {
                    immediate: 布尔值,//默认为false,是否初始化时调用
                    deep: 布尔值,//默认为false,是否开启深度监视
                    handler(newValue, oldValue) {
                        //newValue为新值,oldValue为旧值
                        函数体
                    }
                }
            }
        })

写法2:

        vm.$watch('监视属性名', {
            immediate: 布尔值,//默认为false,是否初始化时调用
            deep: 布尔值,//默认为false,是否开启深度监视
            handler(newValue, oldValue) {
                //newValue为新值,oldValue为旧值
                函数体
            }
        })

在监视属性改变时,会调用handler()函数

深度监视 

配置deep: true时开启深度监视

Vue中watch默认不检测对象内部值的改变

开启深度监视可以检测对象内部值的改变

简写

当deep和immediate均没有配置时,可以简写

简写1:

        const vm = new Vue({
            el: 'div',
            watch: {
                监视属性名(newValue, oldValue): {
                        //newValue为新值,oldValue为旧值
                        函数体
                    }
                }
            })

简写2:

        vm.$watch('监视属性名', function(newValue, oldValue){
                //newValue为新值,oldValue为旧值
                函数体
        })

计算属性与监听属性区别

  • computed相当于watch更加简单
  • computed能完成的,watch均能完成
  • watch能完成的,computed不一定能完成(watch可进行异步操作)

绑定样式

绑定class

1.字符串写法

直接使用v-bind来绑定样式

可以绑定事件,来动态改变类名

        <div class="main" :class="mood">{{text}}</div>
<head>
    <style>
        .main {
            width: 100px;
            height: 100px;
            border: 1px solid black;
        }

        .test01 {
            background-color: aqua;
        }

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

<body>
    <div id="root">
        <div class="main" :class="mood">{{text}}</div>
    </div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                text: '111',
                mood: 'test01',
            }
        })
    </script>
</body>

2.数组写法

用于绑定多个样式

可以通过动态操作数组来控制类

<head>
    <style>
        .work01 {
            font-size: 20px;
        }

        .work02 {
            color: aqua;
        }

        .work03 {
            font-style: italic;
        }
    </style>
    <script src="../vue.js"></script>
</head>

<body>
    <div id="root">
        <div class="main" :class="classArr">{{text}}</div>
    </div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                text: '111',
                classArr: ['work01', 'work02', 'work03']
            }
        })
    </script>
</body>

3.对象写法

用于绑定多个样式,并动态决定用与不用

控制对象中的布尔值,动态决定类的用与不用

<head>
    <style>
        .work01 {
            font-size: 20px;
        }

        .work02 {
            color: aqua;
        }

        .work03 {
            font-style: italic;
        }
    </style>
    <script src="../vue.js"></script>
</head>

<body>
    <div id="root">
        <div class="main" :class="classObj">{{text}}</div>
    </div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                text: '111',
                classObj: {
                    work01: true,
                    work02: true,
                    work03: true,
                }
            }
        })
    </script>
</body>

绑定style

1.直接绑定

直接使用v-bind来绑定样式

语法:

        : style = " { 属性(驼峰命名法) : xxx } "

        xxx为Vue示例中的data数据

    <div id="root">
        <div class="main" :style="{fontSize:fs}">{{text}}</div>
    </div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                text: '1111',
                fs:'20px'
            }
        })
    </script>

2.对象写法

    <div id="root">
        <div class="main" :style="styleObj">{{text}}</div>
    </div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                text: '1111',
                styleObj: {
                    fontSize: '40px',
                    backgroundColor: 'aqua'
                }
            }
        })
    </script>

3.数组写法

需要在数组中配置对象

    <div id="root">
        <div class="main" :style="styleArr">{{text}}</div>
    </div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                text: '1111',
                styleArr: [
                    {
                        fontSize: '40px',
                        backgroundColor: 'aqua'
                    },
                    {
                        color: 'blue'
                    }
                ]
            }
        })
    </script>

条件渲染

v-show

语法:

        v-show = “ 表达式 ”

        表达式值为布尔值

表达式值为false,元素被隐藏,display值为none

v-if

语法:

        v-if = “ 表达式 ”

        后可接v-else-if和v-else

        表达式值为布尔值

表达式值为false时,元素直接被删除,替换为空注释

    <div id="root">
        <div v-show="false">{{test01}}</div>
        <div v-show="true">{{test01}}</div><br>
        <div v-if="false">{{test02}}</div>
        <div v-if="true">{{test02}}</div>
    </div>
    <script>
        const vm=new Vue({
            el:'#root',
            data:{
                test01:'111',
                test02:'222'
            }
        })
    </script>

 列表渲染

v-for遍历

遍历对象

语法:

        v-for = " ( value , key) in 对象名 "  或

        v-for = "  value  in 对象名 "  

        value获取属性值,key获取属性名

    <div id="root">
        <li v-for="(value,key) in Person">
            {{value}}----{{key}}
        </li>
    </div>
    <script>
        const vm=new Vue({
            el:'#root',
            data:{
                Person:{
                    name:'Tom',
                    age:18,
                    shool:'sdau'
                }
            }
        })
    </script>

遍历数组 

语法:

        v-for = " ( value , item) in 数组名 "  或

        v-for = "  value  in 对象名 "  

        value获取值,item获取索引

    <div id="root">
        <li v-for="(value,item) in Arr">
            {{value}}----{{item}}
        </li>
    </div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                Arr: ['A','B','C']
            }
        })
    </script>

key的使用与作用

key的使用

在 v-for遍历 中要添加一个 key属性 

key值的选择,最好使用每条数据的唯一标识

例如ID,身份证,学号等等

如果不添加 key属性 则key值默认为数组或对象的序列号

    <div id="root">
        <li v-for="(p,item) in Person" :key="p.id">
            {{p.name}}---{{p.age}}
        </li>
    </div>
    <script>
        const vm=new Vue({
            el:'#root',
            data:{
                Person:[
                    {id:'001',name:'wu',age:18},
                    {id:'002',name:'liu',age:20},
                    {id:'003',name:'zhao',age:19},
                ]
            }
        })
    </script>

key的作用和虚拟DOM

虚拟DOM是Vue根据数据生成的

key是虚拟DOM对象的标识,可以理解成虚拟DOM对象的唯一ID

当数据发生改变时,Vue会根据新数据生成新虚拟DOM

新旧DOM会进行对比:

1.若有相同的key值:

  • 若虚拟DOM内容没有变,直接使用真实DOM生成页面
  • 若虚拟DOM内容变化,生成新真实DOM替换之前的

2.若没有相同的key值:

  • 直接建立真实DOM,生成页面 

Vue监视数据原理

基本

Vue会监视data中所有有层次的数据

被监视的对象,会添加get()和set()函数,以实现响应式

( get()和set()函数用自Object . defineProperty())

示例:

            data: {
                Person: {
                    name: 'Tom',
                    age: 18,
                    shool: 'sdau'
                },
                Arr: ['A','B','C']
            }

注:数组中的数据没有进行监视,但数组本身被监视

如上图,Arr数组有get()和set()函数

如下图,Arr[0]以及其他数据没有get()和set()函数

关于对象

对象中的数据通过 get()和set()函数 监视

对象中后追加的属性,Vue不会进行监视,即不会有get()和set()函数

如果需要监视追加的属性,则需要用到 特定API

语法:

    Vue.set(对象名,属性名,属性值)  

    另一种写法

    this.$set(对象名,属性名,属性值)

对象名指被添加属性的对象,注意 ' '

示例:给Person对象添加sex属性,属性值为 男

                addSex() {
                    //Vue.set(this.Person,'sex','男')  另一种写法
                    this.$set(this.Person, 'sex', '男')
                }

用此API添加的属性,拥有get()和set()函数,会被监视

注:

        该API,不能给vm或vm的根数据对象(data等)添加属性

关于数组

 因为Vue中数组没有get()和set()函数

所有Vue通过其他方式,来实现数组数据的响应式

在数组中修改数据,必须使用以下方法:

  1. 使用API:push(),pop(),shift(),unshift(),splice(),sort(),reverse()
  2. 使用Vue.set() 或 vm.$set().
    Vue.set(数组名,序列,修改值)  

    另一种写法

    this.$set(数组名,序列,修改值)

只有使用以上方法数组数据才可以实现响应式

当然,也可以直接修改数组本身,因为数组本身有get()和set()函数

过滤器

定义:
        对要显示的数据进行特定格式化后再显示(用于一些简单的逻辑处理)

注册过滤器(两种方式):

        Vue.filters(过滤器名称,function(){
            函数体
        })//全局过滤器

        或

        new Vue({
            filters:{
                过滤器名称(){
                    函数体
                }
            }
        })//局部过滤器

使用过滤器:

过滤器可以用于插值语法 和 v-bind指令

  • {{ xxx | 过滤器名称 }}
  • v-bind:属性 = " xxx | 过滤器名称 " 

常见内置指令

之前接触过的指令

指令 作用 简写
v-bind 单向绑定解析表达式 :xx
v-model 双向数据绑定 v-model="xx"
v-for 遍历数组/对象/字符串
v-on 绑定事件监听 @
v-if / v-else 条件渲染(控制节点是否存在)
v-show 条件渲染(控制节点是否展示)

v-text指令

作用:
        向所在节点渲染文本

    <div id="root">
        <p>{{name}}</p>
        <p v-text="name"></p>
        <p v-text="name">1111</p>
    </div>
    <script>
        new Vue({
            el:'#root',
            data:{
                name:'zhao'
            }
        })
    </script>

注:
        v-text会替换掉节点中的内容

v-html指令

作用:
        向指定节点中渲染包含html结构的内容

        可以识别html结构

    <div id="root">
        <div v-html="test"></div>
    </div>
    <script>
        new Vue({
            el:'#root',
            data:{
                test:'<p>123456</p>'
            }
        })
    </script>

注(v-html有安全性问题):

  1.  在网站上动态渲染任意HTML非常危险,容易导致xss攻击
  2. 在可信内容上使用v-html,不用用在用户提交内容上

v-cloak指令

在网速非常慢的时候,可能会展示未经过Vue渲染的页面

因为网速慢,Vue会加载不出来

可以使用v-cloak指令配合CSS来解决

  • v-cloak没有值
  • v-cloak本质是一个属性
  • v-cloak在Vue加载完成后删除
    <style>
        [v-cloak]{
            display: none;
        }
    </style>


    <div id="root">
        <div v-cloak>111</div>
    </div>

v-once指令

  • v-once没有值
  • v-once所在节点只进行初次动态渲染
  • 初次动态渲染完成后,变为静态内容
  • 以后数据变化不会引起所在结构改变

v-pre指令

用于跳过其所在节点的编译过程

        <p v-pre>{{name}}</p>

自定义指令 

函数式

自定义指令可以通过配置到Vue实例里,directives对象中来实现

语法:

        new Vue({
            directives:{
                指令名(element,binding){
                    函数体
                }
            }
        })

其中传入的参数:

element为绑定自定义指令标签的DOM对象

binding为DOM对象的相关属性

绑定自定义指令时要加v-

    <div class="root">
        <p v-test01>1111</p>
    </div>
    <script>
        const vm=new Vue({
            el:'.root',
            directives:{
                test01(element,binding){
                    console.log(element,binding)
                }
            }
        })
    </script>

自定义指令中函数调用时机:

  • 指令与元素成功绑定时函数调用
  • 指令所在模板重新解析时函数调用

注:命名如果有多个单词,使用 - 链接

对象式

对象式自定义指令函数调用时机更加细致,会比函数时多一次调用

语法:

        new Vue({
            directives:{
                指令名:{
                    bind(element,binding){
                        函数体
                    },
                    inserted(element,binding){
                        函数体
                    },
                    update(element,binding){
                        函数体
                    }
                }
            }
        })

传入的参数同函数式

函数调用时机:

  • bind函数:指令与元素绑定成功时调用
  • inserted函数:指令所在元素被插入页面时调用
  • update函数:指令指令所在模板重新解析时调用

全局自定义指令

通过在Vue实例中配置directives对象所设置的为局部指令

全局自定义指令语法:
        Vue.directive(指令名,配置对象)

        Vue.directive(指令名,回调函数)

    <div class="root">
        <div v-test03>33</div>
        <div v-test04>44</div>
    </div>
    <script>
        Vue.directive('test03', {
            bind(element, binding) {
                console.log(element, binding)
            },
            inserted(element, binding) {
                console.log(element, binding)
            },
            update(element, binding) {
                console.log(element, binding)
            }
        })

        Vue.directive('test04',function(element, binding){
            console.log(element, binding)
        })
    </script>

生命周期

概念

生命周期 又名:生命周期回调函数,生命周期函数,生命周期钩子

生命周期本质上就是函数

Vue会在特殊时刻帮我们调用的一些特殊名称的函数

生命周期函数名字不可更改,其配置在Vue实例中,其中this指向Vue示例

示例(以mounted为例):

    <script>
        const vm = new Vue({
            el: '#root',
            mounted() {
                console.log(this)
            },
        })
    </script>

“Vue的一生”

创建(出生)

Vue刚刚创建,仅完成初始化,此时数据监测和数据代理创建还未开始;

初始化完成后,进行数据监测和数据代理创建时,会调用两个生命周期函数(不是Vue创建时)

此时 模板未解析

1.数据监测和数据代理之前,初始化后:

  • 调用beforeCreate函数
  • 此时无法通过vm访问data中的数据,methods中的方法
  • 因为数据监测和数据代理未完成

2.数据监测和数据代理之后,解析模板之前:

  • 调用created函数
  • 此时可以通过vm访问data中的数据,methods中的方法
  • 因为模板未解析,页面不会显示解析内容
挂载(生活)

挂载即把初始的真实DOM元素放到页面

在Vue解析完模板,生成虚拟DOM后,进行虚拟DOM转真实DOM

此时调用两个生命周期函数

1.解析完模板后,挂载完成前:

  • 调用beforeMount函数
  • 页面此时呈现未未经Vue编译的DOM
  • 所有对DOM的操作,最终都不会奏效

2.挂载完成后:

  • 调用mounted函数
  • 对DOM的操作有效,但尽量避免
  • 此时可以进行一些初始化操作,比如开启定时器,发送网络需求等
更新(颠覆三观)

在数据更新时,会调用两个生命周期函数

1.新虚拟DOM生成前:

  • 调用beforeUpdate函数
  • 此时Data数据是新的,但是页面没有进行更新

2.页面更新后:

  • 调用updated函数
  • 此时Data数据和页面都是新的
销毁(逝世)

Vue可以通过Vue.$destroy()来销毁

销毁时会调用两个生命周期函数

1.销毁前(回光返照):

  • 调用beforeDestroy函数
  • 此时Vue的所有配置都还可以使用,即将销毁
  • 一般在此阶段进行关闭定时器,取消订阅的收尾操作

2.销毁后(已寄):

  • 可以调用destroyed函数,但Vue以及没有

注意事项

常用生命周期钩子:

  1. mounted:初始化操作
  2. beforeDestroy:收尾操作

关于销毁Vue:

  • 销毁后自定义事件失效,但原生DOM事件依然有效
  • 一般不会使用beforeDestroy操作Data数据,因为用不到了

Vue组件化编程

组件

实现应用中局部功能代码资源集合

扒一张官网的图(下图):

在一个大盒子中可以分三个中盒子(灰色) ,中盒子又可以分成几个小盒子(深灰色)

这几个盒子中代码和资源的集合可以视为一个组件

作用:组件可以复用编码,简化项目编码

当应用中的功能都是多组件的方式来编写的,那这个应用就是一个组件化的应用

非单文件组件

特点:一个文件中包含多个组件

Vue使用组件有三步:

  1. 定义组件(创建组件)
  2. 注册组件
  3. 使用组件

定义组件(创建组件)

定义组件需要使用Vue.extend()在全局中进行定义

其中()中传入的内容与 new Vue()需要传入的几乎一模一样

区别:

  • 不写el配置:最终所有组件由vm中的el来决定服务于那个容器
  • data必须写出函数式:避免数据存在引用关系
  • 使用template配置组件结构

示例:

        //定义组件
        const Person = Vue.extend({
            template: `
            <div>
                <p>{{name}}--{{age}}</p>
            </div>
            `,
            data() {
                return {
                    name: 'zhao',
                    age: '18'
                }
            }
        })

注册组件

1.局部注册

在Vue实例中配置components对象

        const vm = new Vue({
            el: '#Root',
            //注册局部组件
            components: {
                Person: Person
                //简写:Person
            }
        })

2.全局注册

语法:

        Vue.component('组件名', 组件)

        //注册全局组件
        Vue.component('School', School)

使用组件

在HTML中编写组件标签即可

    <div id="Root">
        <!--使用组件-->
        <Person></Person>
        <br>
        <School></School>
    </div>

示例代码

<body>
    <div id="Root">
        <!--使用组件-->
        <Person></Person>
        <br>
        <School></School>
    </div>
    <script>
        //定义组件
        const Person = Vue.extend({
            template: `
            <div>
                <p>{{name}}--{{age}}</p>
            </div>
            `,
            data() {
                return {
                    name: 'zhao',
                    age: '18'
                }
            }
        })
        const School = Vue.extend({
            template: `
            <div>
                <p>{{name}}--{{address}}</p>
            </div>
            `,
            data() {
                return {
                    name: 'sdau',
                    address: '泰安'
                }
            }
        })
        //注册全局组件
        Vue.component('School', School)

        const vm = new Vue({
            el: '#Root',
            //注册局部组件
            components: {
                Person: Person
                //简写:Person
            }
        })
    </script>
</body>

注意事项

组件名
  • 组件名如果由多个单词组成,使用kebab-case命名法:my-school
  • 也可以使用CamelCase命名:MySchool,但是需要Vue脚手架
  • 可以使用name配置指定组件在开发者工具中呈现的名字
        const School = Vue.extend({
            name:'xxx',
            template: `
            <div>
                <p>{{name}}--{{address}}</p>
            </div>
            `,
            data() {
                return {
                    name: 'sdau',
                    address: '泰安'
                }
            }
        })

简写

        const School = Vue.extend(options)

简写为:

        const School = options

组件其他事项

组件的嵌套

组件可以注册在组件中,实现组件的嵌套

示例:

<body>
    <div id="Root">
        <App></App>
    </div>
    <script>
        const School = Vue.extend({
            template: `
                <div>{{name}}--{{address}}</div>
            `,
            data() {
                return {
                    name: 'sdau',
                    address: '泰安'
                }
            }
        })
        const Person = Vue.extend({
            template: `
                <div>{{name}}--{{age}}</div>
            `,
            data() {
                return {
                    name: 'zhao',
                    age: 18
                }
            }
        })

        const App = Vue.extend({
            template: `
            <div>
                <Person></Person>
                <School></School>
            </div>
            `,
            components: {
                School,
                Person
            }
        })

        const vm = new Vue({
            el: '#Root',
            components: {
                App
            }
        })
    </script>
</body>

VueComponent

组件的本质是一个名为VueComponent构造函数,由Vue.extend生成

在我们使用组件时,Vue会解析并创建出组件的实例对象

即Vue会帮我们调用 new VueComponent(options)

另外,每次调用Vue.extend,返回值都是一个全新的VueComponent

关于this指向:

  • 组件配置中,this指向均是【VueComponent实例对象】
  • new Vue()配置中,this指向均是【Vue实例对象】

一个内置关系

在Vue中,组件原型对象 的 原型 为Vue示例的 原型对象

即:VueComponent.prototype.__proto__===Vue.prototype

可以让组件实例对象可以访问到Vue原型上的属性和方法

单文件组件

特点:一个文件中只包含1个组件

将非单文件组件中的多个组件进行拆分,写到多个文件中,每个文件就是一个组件

每个组件文件的后缀为 .vue

通过ES6模块化语法进行导出和引入

导出 

组件文件写法:

其中使用 export default { } 来导出组件

<template>
  <div>
    模板
  </div>
</template>

<script>
    //导出
    export default {
        Vue配置项
    };
</script>

<style>
    样式 /*可省略*/
</style>

示例:

<template>
  <div class="test">
    <h2>{{ name }}--{{ address }}</h2>
    <button @click="show">123456</button>
  </div>
</template>

<script>
export default {
  name: "SchoolName",
  data() {
    return {
      name: "sdau",
      address: "山东泰安",
    };
  },
  methods: {
    show() {
      alert("123456");
    },
  },
};
</script>

<style>
.test {
  background-color: aqua;
}
</style>

引入

引入可以使用 import···from··· 来引入

import 组件名 from "组件地址";

示例:

<template>
  <div>
    <PersonName></PersonName>
    <SchoolName></SchoolName>
  </div>
</template>

<script>
import PersonName from "./components/PersonName.vue";
import SchoolName from "./components/SchoolName.vue";
export default {
  name: "App",
  components: {
    PersonName,
    SchoolName,
  },
};
</script>

ref属性

ref属性用来给元素或子组件注册引用信息(id属性的替代者

语法:

  • 标记(示例):<h1 ref="xxx">···<h1> 或 <组件名 ref="xxx">···</组件名>
  • 获取:this.$refs.xxx

关于获取值:

  • 应用在html标签上,获取真实DOM元素
  • 应用在组件标签上,获取组件示例对象
<template>
  <div id="app">
    <img ref="img" alt="Vue logo" src="./assets/logo.png" />
    <HelloWorld ref="hello" msg="Welcome to Your Vue.js App" />
    <button @click="show">按钮</button>
  </div>
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";

export default {
  name: "App",
  components: {
    HelloWorld,
  },
  methods: {
    show() {
      console.log(this.$refs);//返回对象
      console.log(this.$refs.img);//返回真实DOM
      console.log(this.$refs.hello);//返回组件示例对象
    },
  },
};
</script>

配置项props

配置项props可以让组件接受外部传来的数据

当父组件引入其他组件,父组件所需数据子组件没有时,

可以使用配置项props,通过父组件给子组件数据赋值

是一种组件间的通信方式,将数据从父组件传递给子组件

传递与接收

1.传递方(父组件):

    <子组件名 数据名=" 值 "/>

将传入的值传递给子组件

2.接收方(子组件):

  props:['数据名']

接收到父组件传递来的数据

示例:

  • App.vue(传递方,部分代码):
<template>
  <div id="app">
    <MyPerson name="zhao"/><!--传递-->
  </div>
</template>
  • MyPerson(接收方,部分代码):
<template>
  <div>
    <h1>{{ name }}--{{ age }}</h1>
  </div>
</template>

<script>
export default {
  name: "MyPerson",
  data() {
    return {
      age: 18,
    };
  },
  props:['name']//接收
};
</script>

接收的三种方法

接收数据共有三种方法:

1.只接收

  props:['name']

2.限制类型

  props:{
    name:String//限制name为字符串
  }

3.限制类型与必要性,指定默认值

  props: {
    name: {
      type: String, //限制name为字符串
      required: true, //必要性
      default: "zhao", //默认值
    },
  },

必要性:父组件必须传值

必要性与默认值一般不在一起使用,有矛盾

mixin(混入)

功能:将多个组件共用的配置,进行提取,成为一个混入对象,进行使用

定义混入

混入可以定义在另一个JS文件中

即多个组件都有的配置,提取到一个JS文件中

export const test={
    methods:{
        show(){
            console.log(this.name);
        }
    }
}

通过导出,以便于组件使用

使用混入

局部混入

在引入混入后,可以通过mixins配置项来使用混入

<template>
    <div>
        <h1>
            {{name}}--{{age}}
        </h1>
        <button @click="show">123</button>
    </div>
</template>

<script>
import {test} from './mixin.js'

export default {
    name:'MyPerson',
    data(){
        return{
            name:'zhao',
            age:18
        }
    },
    mixins:[test]
}
</script>

全局混入

可以直接在App组件中引入混入,使用Vue.mixin(xxx)来进行全局混入

import {test} from './components/mixin'
Vue.mixin(test)

插件

功能:用于增强Vue

本质:插件本质上是一个对象,其中含有一个install方法

install的第一个参数是Vue,所有插件可以实现各种功能

定义插件

对象名.install = function(Vue){
  函数体
}

使用插件

Vue.use(对象名)

scoped样式

在Vue组件化编程中,每个组件的样式也会影响到其他的组件,产生冲突

作用:

        可以让样式在局部生效,防止冲突

写法:

        <style scoped>

组件自定义事件

组件自定义事件是一种组件间的通信方式,将数据从子组件传递给父组件

绑定

绑定自定义事件都是在父组件中

方法一

直接在父组件中,给子组件绑定事件

    <MyPerson @gainP="getPersonName" />

如上,为子组件MyPerosn绑定名为gainP的事件

触发事件调用getPersonName回调函数

方法二

在生命周期钩子中绑定,用到ref属性

    <MySchool ref="dome" />

    ......

    mounted() {
      this.$refs.dome.$on("gainS", this.getSchoolName);
    },

如上,为子组件MySchool绑定名为gainS的事件

触发事件调用getSchoolName回调函数

整体代码示例(父组件):

<template>
  <div id="app">
    <p>{{ PersonName }}//{{ SchoolName }}</p>
    <MyPerson @gainP="getPersonName" />
    <MySchool ref="dome" />
  </div>
</template>

<script>
import MyPerson from "./components/MyPerson.vue";
import MySchool from "./components/MySchool.vue";
export default {
  name: "App",
  components: {
    MyPerson,
    MySchool,
  },
  data() {
    return {
      PersonName: "",
      SchoolName: "",
    };
  },
  methods: {
    getPersonName(test) {
      this.PersonName = test;
    },
    getSchoolName(test) {
      this.SchoolName = test;
    },
  },
  mounted() {
    this.$refs.dome.$on("gainS", this.getSchoolName);
  },
};
</script>

触发

触发自定义事件都是在子组件中

通过触发原生事件来触发自定义事件

语法:

      this.$emit("自定义事件名", 传入数据);

示例: 

  <button @click="givePerosnName">触发事件</button>

  ......

  methods: {
    givePerosnName() {
      this.$emit("gainP", this.Name);
    },
  },

如上,触发按钮点击事件,调用givePerosnName函数

在givePerosnName函数中触发gainP自定义事件

并且传入数据this.Name

解绑

解绑事件也是在子组件中

语法:

      this.$off("自定义事件名");

 示例:

  <button @click="relieve">解绑事件</button>

  ......

  methods: {
    relieve() {
      this.$off("gainP");
    },
  },

如上,触发按钮点击事件,调用relieve函数

在relieve函数中解绑gainP自定义事件

其他

  • 组件可以绑定原生DOM事件,但是需要 native 修饰符
  • 通过方法二来绑定事件时,回调函数要配置在methods中,或用箭头函数,这样this指向正常,为父组件。使用普通函数,this指向子组件。

全局事件总线

全局事件总线是一种组件之间的通信方式,可以用于任意组件间通信

其并非Vue所提供的方法,而是一种思路,所以写法不唯一

核心思想是找到一个各个组件均可进行通信的中介,来进行任意组件间的通信

虽然写法不唯一,但只记一种

安装全局事件总线

中介可以创建在Vue实例对象的原型上

通过生命周期钩子在Vue实例对象创建完成前安装

在main.js中

new Vue({
  render: h => h(App),
  beforeCreate() {
    Vue.prototype.$bus = this
  }
}).$mount('#app')

如上:创建名为$bus的中介

使用全局事件总线

接收

接收数据要在接收方组件中,给中介$bus绑定自定义事件

事件的回调配置在接收方组件中

  methods: {
    getPersonName(test) {
      this.PersonName = test;
    },
  },
  mounted() {
    this.$bus.$on('gainP',this.getPersonName)
  },

如上:

为$bus绑定gainP的自定义事件,函数回调在组件methods配置中

另外,最好在beforeDestroy生命周期钩子中,用$off解绑当前组件所用事件

提供

提供数据只需要在提供方触发$bus的自定义事件即可

      this.$bus.$emit("gainP", this.Name);

消息订阅与发布

消息订阅与发布是一种组件之间的通信方式,可以用于任意组件间通信

1.安装

需要安装pubsub

    npm i pubsub-js

2.引入

import pubsub from 'pubsub-js'

3.接收数据

  methods: {
    dome(){
      ......
    }
  },
  mounted(){
    this.pid=pubsub.subscribe('xxx',this.dome)//订阅消息
  }

4.提供数据

    pubsub.publish('xxx',数据)

5.取消订阅

    PubSub.unsubscribe(pid)

插槽

作用:是一种组件间的通信方式,可以让父组件向子组件指定位置插入HTML结构

           是一种将数据从父组件传递给子组件的方式

默认插槽

父组件(传递方):

设置要传给子组件的HTML结构,需要在使用组件时,在组建标签内写入

    <MyTest>
      <div>Hello World</div>
    </MyTest>
    <!--MyTest为组件名-->

子组件(接收方):

在子组件中需要进行定义插槽,确定传入HTML的插入位置

HTML结构会插入在<slot>标签的位置

<template>
  <div>
    <slot>默认内容</slot>
    <!--如果没有传入HTML,则渲染slot标签内的默认内容-->
  </div>
</template>

 具名插槽

当需要插入多个插槽时,可以用具名插槽

子组件(接收方):

子组件定义多个插槽,并配置name

<template>
  <div>
    <slot name=dome1>默认内容</slot>
    <slot name=dome2>默认内容</slot>
  </div>
</template>

父组件(传递方):

给传递的HTML结构配置solt,值为插槽的name值

    <MyDome>
      <div slot='dome1'>Hello World</div>
      <div slot='dome2'>Hello World</div>
    </MyDome>

作用域插槽

当需要使用的data数据不在传递方父组件自身,而在子组件里时

子组件(接收方):

子组件在定义插槽时,将数据传入插槽中

<template>
  <div>
    <slot :word='word'>默认内容</slot>
  </div>
</template>

<script>
export default {
  name: "TheDome",
  data() {
    return {
      word: "Hello World",
    };
  },
};
</script>

 父组件(传递方):

必须使用<template>标签,并在标签内配置scope

    <TheDome>
      <template scope="scopeData"> 
        <div>{{ scopeData.word }}</div>
      </template>
    </TheDome>

如上, 子组件传递的数据,以对象形式,给了scopeData

nextTick

语法:

      this.$nextTick(()=>{
        回调函数
      })

作用:

        在下一次DOM更新后执行回调

        用于改变数据后,基于更新后的新DOM进行操作时

Vue封装的过渡与动画

如图所示:

Vue封装的过渡与动画分为进入(enter)离开(leave)

进入和离开又分为起点过程中(active)终点(to)

类名

  • 元素进入:
  1. v-enter:进入起点
  2. v-enter-active:进入过程
  3. v-leave-to:进入终点
  • 元素离开:
  1. v-leave:离开起点
  2. v-leave-active:离开过程
  3. v-leave-to:离开终点

在Vue中添加过渡与动画,需要给Vue所配置的类添加样式

过渡/动画

过渡的添加,需要六种样式全部配置

.v-enter,
.v-leave-to {
  transform: translateX(-100%);
}
.v-enter-to,
.v-leave {
  transform: translateX(0);
}
.v-enter-active,
.v-leave-active {
  transition: 1s;
}

使用 <transition>标签将要进行过度的元素进行包裹

    <transition>
      <div class="dome" v-show="xxx">111</div>
    </transition>

被包裹的元素会自行执行样式中所写的过渡

动画的添加,只需要配置v-enter-active和v-leave-active即可,其他同过渡

.v-enter-active {
  animation: 1s test01;
}
.v-leave-active {
  animation: 1s test01 reverse;
}
@keyframes test01 {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0);
  }
}

多个元素过渡

相同样式:

多个元素,共用一个过渡,需要用 <transition-group>标签包裹

并且每个元素都要指定key值

    <transition-group> 
      <div class="dome" v-show="xxx" key="1">111</div>
      <div class="dome" v-show="xxx" key="2">222</div>
    </transition-group>

不同样式:

多个元素,不共用一个过渡

要为<transition>标签配置name属性

    <transition name='test01'>
      <div class="dome" v-show="xxx">111</div>
    </transition>

在写样式时,类名中的v换成name值

例:

.test01-enter,
.test01-leave-to {
  transform: translateX(-100%);
}

Vue中的Ajax

Vue脚手架配置代理

配置代理可以解决开发环境 Ajax 跨域问题

在vue.config.js中添加配置

  devServer:{
    proxy:'xxx'//xxx为服务器基础路径