admin 管理员组

文章数量: 887021

1.Vue核心

1.1 初识Vue

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

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

3.root容器里的代码被称为“Vue模板”。

4.容器和实例的关系:容器和实例只能是一一对应的关系。开发中只会出现一个vue实例

a.多个容器对应一个实例,只有第一个容器会绑定。

b.多个实例对应一个容器,只有第一个实例会生效。

5.JS表达式和JS代码

a.JS表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方,例如:a,a+b,demo(1),x==y?"A":"B".

b.js代码(语句):只控制走向不产生值

if(){},for(){}

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

7.一旦data中的数据发生变化,那么模板中用到该数据的地方也会自动更新。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../VUE/js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h1>
            <!-- 差值语法 -->
            {{name}}
        </h1>
    </div>
    <div>
        <h1>
            <!-- 差值语法 -->
            {{name}}
        </h1>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip=false;

        //创建vue实例
        new Vue({
            el: '#root',//el用于指定vue实例为哪个容器服务,值通常为css选择器字符串/也可以写document.getelementById()(一般不用)
            data: {
                //data内的数据仅供el绑定的元素使用
                name: "hello word",
            }
        })
        
    </script>
</body>
</html>

1.2 模板语法

 1.2.1 差值语法

差值语法只管标签内容,例如:上方代码中的yyyy是标签体内容。

写法{{yyyy}},yyyy是js表达式,且可以读取到data中的所有属性。

<h1 x="xxxx">
    yyyy 
</h1>

1.2.2 指令语法

指令语法只管标签属性,例如:上方代码中的xxxx是标签属性。

功能:用于解析标签(包括:标签属性,绑定事件等等),xxxx也是是js表达式,且可以读取到data中的所有属性。

如果属性前面加了v-bind,那么属性后面的“xx”内容,就会被当做表达式来执行。

v-bind:可以简写为:

备注:vue中有很多指令,且形式都是:v-***,此处v-bind只是举例子使用。

    <div id="root">
        <h1>
             差值语法 <br>
           你好 {{name}}
        </h1>
        <h1>
            指令语法 <br>
        <h1>
            指令语法 <br>
          <a v-bind:href="url">百度</a>
          <a :href="url">百度</a>
       </h1>
       </h1>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip=false;
        //创建vue实例
        new Vue({
            el: '#root',//el用于指定vue实例为哪个容器服务,值通常为css选择器字符串/也可以写document.getelementById()(一般不用)
            data: {
                //data内的数据仅供el绑定的元素使用
                name: "jack",
                url: "https://www.baidu"
            }
        })
    </script>

1.3数据绑定

vue有两种绑定方式:单向绑定和双向绑定

<div id="root">
       单向数据绑定 <input type="text" v-bind:value="oneway"><br>
       双向数据绑定 <input type="text" v-model:value="twoway"><br>
       <!-- 简写 -->
       单向数据绑定 <input type="text" :value="oneway"><br>
       双向数据绑定 <input type="text" v-model:="twoway"><br>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip=false;
        //创建vue实例
        new Vue({
            el: '#root',//el用于指定vue实例为哪个容器服务,值通常为css选择器字符串/也可以写document.getelementById()(一般不用)
            data: {
                //data内的数据仅供el绑定的元素使用
                oneway: "one-way",
                twoway: "two-way"
            }
        })
    </script>

1.3.1单向绑定(v-bind:)

数据只能从data流向页面,无法从页面影响data

1.3.2双向绑定(v-model:)

数据可以从data流向页面,页面也可以流向data

1.3.3备注

1.双向绑定一般都应用在表单类元素上如:input,select,redio等等,有value的元素,因为只有用户有操做的元素,才有可能改变vaule,才有双向绑定的意义。类似于<h1>等标签,用户无法修改value,自然也没有双向绑定的意义。

2.v-model:value可以简写为v-model,因为v-model默认收集的就是value属性值。

1.3.4 简写

v-bind:value简写为:value

v-model:value简写为:v-model:

1.3.5 el与data的两种写法

1.3.5.1 el写法

(1)new Vue的时候配置el属性

(2)先创建Vue实例,接收以后通过v.$mount("#id")对el进行绑定

1.3.5.2 data写法

(1)对象式

(2)函数式

在组件中,data必须使用函数式否则会报错。

1.3.5.3 注意:

在Vue管理的函数中,一定不要用箭头函数,否则this就不再是Vue实例了,而是Window。

    <div id="root">
      <h1>{{name}}</h1>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip=false;
        //创建vue实例
       const vue= new Vue({
            //el: '#root',//第一种写法

            //data第两种写法
            /*data第一种写法 对象式
            data: {
               name: "hello word"
            }*/


            // data第二种写法 函数式
            data(){
                return {
                    name: "hello word"
                }
            }
        })
        vue.$mount('#root')//第二种写法
    </script>

1.4 MVVM模型

1.4.1 概念

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

2.V:视图(View)对应模板

3.VM:视图模型(ViewMode)对应Vue实例对象


    <div id="root">
      <h1>学校名称:{{name}}</h1>
      <h1>学校地址:{{address}}</h1>
      <h1>VM属性:{{$createElement}}</h1>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip=false;
        //创建vue实例
       const vm= new Vue({
            el: '#root',//第一种写法
            data: {
                name: "华盛顿第一人民大学",
                address: "华盛顿南街123号",
            }
        })
        console.log(vm)
    </script>

1.4.2结论

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

(2)vm上的所有属性以及vue圆形上的所有属性,在vue模板中都可以直接使用

1.5 数据代理

1.5.1 Object.defineproperty方法

 <div id="root">
    </div>
    <script type="text/javascript">
        let num=18;
       let person={
        name:"张三",
        sex:"男",
         age: num
       }
       /*增加age属性      
       入参:
            1.增加属性的对象
            2.增加的属性名称
            3.属性的配置       
       */
       Object.defineProperty(person,"age",{
        /*
        value:18,
        enumerable:true,//可以枚举
        writable:true,//是否可以修改
        configurable:true//是否可以被删除
        */
       //当有人读取person的age时调用
        get:function(){
            //return "hello"
            console.log("有人读取了age属性")
            return num;
        },
        //当有人修改person的age时调用
        set:function(value){
            //return "hello"
            console.log("有人修改了age属性")
            num=value;
        }
       })
       console.log(person)
       /*把object转为数组
         1. 如果enumerable为false:
            Object.keys(person)无法取值值age,
            如果enumerable为true:
            Object.keys(person)可以取值age,
            默认false
        2.  如果writable为false:
            则属性不可被修改
            如果writable为true:
            则属性可被修改
            默认false
        3.  configurable是否可以被删除同以上两个
       */
    //console.log(Object.keys(person))
    </script>

1.5.2 数据代理概念

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

    <script type="text/javascript">
        let objx={x:100}
        let objy={y:200}
        /*
            通过objy操作objx的x属性
            给objy添加一个x属性 
            get方法返回值为objx.x
            set方法修改bjx.x

        */
       Object.defineProperty(objy,"x",{
        get: function(){
            return objx.x;
        },
        set:function(value){
            objx.x=value
        }
       })
    </script>

1.5.3 Vue中的数据代理 

(1)Vue中的数据代理:通过vm对象来代理data中属性的操作(读/写)。

(2)Vue中数据代理的好处:更加方便的操作(读/写)data中的数据。

(3)基本原理:通过Object.defineProperty()把data对象中的所有属性都加到vm上,为每一个添加到vm身上的属性都制定一个getter/setter方法,在getter/setter方法内部去操作(读/写)data中的属性。

具体内容参考后面的数据劫持。

1.6事件处理

1.6.1事件的基本处理

事件的基本使用:

(1)使用v-on:xxx 或者@xxx绑定事件,其中xxx是事件名

(2)事件的回调需要配置在methods对象中,最终会在vm桑。

(3)methods中配置的函数,不要使用箭头函数,否则this指向的不是vue而是window

(4)methods中配置的函数都是被vue所管理的函数,this指向vm或者组件实例对象

(5)@click="demo"和@click="demo($event)"效果是一样的,但是后者有传参。

<div id="root">
        <h2>欢迎来到{{address}}</h2>
        <!-- <button v-on:click="showInfo">点我提示信息</button> -->
        <button @click="showInfo1">点我提示信息1(不传参)</button>
        <button @click="showInfo2($event,66)">点我提示信息2(传参)</button>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',//第一种写法
            data: {
                address: "华盛顿第一人民医院",
            },
            methods: {
                /* showInfo: (event) => {
                    //所有被vue管理的函数最好都不要写箭头函数
                    //console.log(this)//如果用箭头函数定义this对象为window
                } */
                showInfo1(event) {
                    
                    //console.log(this==vm)//true this是vue对象
                    //获取时间源对象
                    /* console.log(event.target) */ 
                    alert("1点我提示信息1")               
                },
                showInfo2(event,number) {
                    console.log(event)
                    console.log(number)
                    //console.log(this==vm)//true this是vue对象
                    //获取时间源对象
                    /* console.log(event.target) */ 
                    alert("2点我提示信息2")               
                },
            }
        })

    </script>

1.6.2 事件修饰符

Vue中的事件修饰符

(1)prevent:阻止默认事件(常用)。

(2)stop:组织事件冒泡(常用)。

(3)once:事件只能触发一次(常用)。

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

(5)self:只有event.target是当前操作的元素才会触发的时间。

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

  <div id="root">
        <h2>欢迎来到{{address}}</h2>
        <!-- 阻止默认行为 -->
        <!-- <a href="https://www.baidu" @click="showInfo">点我提示信息</a> -->
        <a href="https://www.baidu" @click.prevent="showInfo">点我提示信息</a>
        <div class="demo1" @click="showInfo">
            <!-- 阻止冒泡 -->
            <button @click.stop="showInfo">阻止冒泡</button>
        </div>
        <!-- 事件只触发一次 -->
        <button @click.once="showInfo">事件只触发一次</button>
        <!-- 阻止事件捕获 -->
        <div class="box1" @click.capture="showMsg(1)">
            div1
            <div class="box2" @click="showMsg(2)">
                div2
            </div>
        </div>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',//第一种写法
            data: {
                address: "华盛顿第一人民医院",
            },
            methods: {
                showInfo(event) {
                    //阻止事件默认行为
                    //event.preventDefault()
                    alert("你好")
                },
                showMsg(n) {
                    alert("n=" + n)
                },
            }
        })

    </script>
    <style>
        .demo1 {
            height: 100px;
            width: 200px;
            background-color: black;
        }

        .box1 {
            padding: 5px;
            background-color: aqua;

            .box2 {
                background-color: orange;
            }
        }
    </style>

1.6.3键盘事件

(1)Vue中常用的按键别名:

                回车=》enter

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

                退出=》esc

                空格=》space

                换行=》tab(特殊,必须配合keyDown使用)

                上=》up

                下=》down

                左=》left

                右=》right

(2)Vue未提供别名的按键可以用原始的key值去绑定,但要注意转为kebab-case(短横线命名)

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

       (3.1)配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。

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

(4)也可以用keyCode去指定按键(不推荐)。

(5)Vue.config.keyCodes.自定义键名=键码,可以定制按键别名。

    <div id="root">
        <input type="text" placeholder="按下回车提示输入" @keyup="showInfo">
        <input type="text" placeholder="按下回车提示输入" @keyup.hc="showInfo">
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        Vue.config.keyCodes.hc=13//定义一个别名
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
            },
            methods: {
                showInfo(event){
                    //if(event.keyCode!=13)return;
                    console.log(event.key,event.keyCode)
                }
            }
        })

    </script>

1.6.4总结

1.7.2的修饰符可以连续使用:@click.passive.stop="demo"

1.7.3系统修饰键后面也可以连续使用:@keyUp.ctrl.y="demo"

1.7 计算属性与监视

当页面有两个输入框,且另一个位置需要计算两个框之内的内容时:

1.7.1计算属性

1.7.1.1 差值语法

    <div id="root">
       姓<input type="text" placeholder="请输入姓" @keyup.enter="firstNameChange"><br>
       名<input type="text" placeholder="请输入名" @keyup.enter="lastNameChange"><br>
       姓名:{{firstName}}{{lastName}}
       
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                firstName:"",
                lastName:"",
            },
            methods: {
                firstNameChange(event){
                   this.firstName= event.target.value.slice(0,3)
                },
                lastNameChange(event){
                    this.lastName= event.target.value
                    if(this.lastName!=""){
                        this.lastName="-"+ this.lastName
                    }

                }
            }
        })

    </script>
1.7.1.2计算属性

计算属性:

(1)定义:要用的属性不存在,要通过已有属性进行计算。

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

(3)get函数什么时候执行:

        (3.1)初次读取属性时会执行一次。

        (3.2)当以来的属性,数据发生改变时,会再次调用。

(4)优势:与methods实现相比,内部有缓存机制(可服复用),效率更高,调试方便。

(5)备注:

        (5.1)计算属性最终会出现在vm上,直接读取即可使用。

        (5.2)如果计算属性要被修改,那么必须写set函数去响应修改,且

set中要引起计算式依赖的数据发生改变。


    <div id="root">
       姓<input type="text" placeholder="请输入姓" @keyup.enter="firstNameChange"><br>
       名<input type="text" placeholder="请输入名" @keyup.enter="lastNameChange"><br>
       姓名:{{fullName}}
       
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                firstName:"",
                lastName:"",
            },
            computed:{
                fullName:{
                    //当游人读该属性时,他就回去执行该属性的get方法
                    get(){
                        console.log("get方法被调用");
                        console.log()
                       let fullName= this.firstName+(this.lastName==""?"":"-"+this.lastName);
                        return fullName;
                    },
                    //当修改该属性时,他就回去执行该属性的set方法
                    set(value){
                        console.log("set方法被调用");
                        const arr=value.split("-");
                        this.firstName=arr[0];
                        this.lasstName=arr[1];
                        return fullName;
                    }

                }
            },
            methods: {
                firstNameChange(event){
                   this.firstName= event.target.value
                },
                lastNameChange(event){
                    this.lastName= event.target.value
                }
            }
        })

    </script>

1.7.1.3计算属性简写

当计算属性只考虑读取,不考虑编辑时计算属性可以简写:


    <div id="root">
       姓<input type="text" placeholder="请输入姓" @keyup.enter="firstNameChange"><br>
       名<input type="text" placeholder="请输入名" @keyup.enter="lastNameChange"><br>
       姓名:{{fullName}}
       
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                firstName:"",
                lastName:"",
            },
            computed:{
                //当只有获取属性没有设置属性时
                fullName(){
                       let fullName= this.firstName+(this.lastName==""?"":"-"+this.lastName);
                        return fullName;
                }
            },
            methods: {
                firstNameChange(event){
                   this.firstName= event.target.value
                },
                lastNameChange(event){
                    this.lastName= event.target.value
                }
            }
        })

    </script>

1.7.2 监视属性

1.7.2.1监视属性两种写法

监视属性watch:

(1)当贝坚实的属性发生变化是,回电好书自动调用,执行相关代码。

(2)监视的属性必须存在才能监视

(3)监视属性的两种写法:

        (3.1).new Vue是传入watch配置

        (3.2)通过vm.$watch监视

     <div id="root">
        <h1>今天天气很{{info}}</h1>
        <button @click="editIsHot">
            切换天气
        </button>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                isHot: true,
            },
            computed: {
                info() {
                    return this.isHot ? "炎热" : "凉爽";
                }
            },
            methods: {
                editIsHot() {
                    this.isHot = !this.isHot
                }
            },
            /* watch:{
                isHot:{
                    immediate:true,//初始化时让handler调用一下
                    handler(newValue,oldValue){
                        console.log("isHot被修改了,newValue="+newValue+",oldValue="+oldValue)
                    }
                }
            } */

        })
        vm.$watch('isHot', {
            immediate: true,//初始化时让handler调用一下
            handler(newValue, oldValue) {
                console.log("isHot被修改了,newValue=" + newValue + ",oldValue=" + oldValue)
            }
        })
    </script>

1.7.2.2深度监视

深度监视:

(1)VUe中的watch默认部件是对象内部值得改变。(一层)

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

备注:

(1)Vue自身可以检测对象内部指的改变,但是Vue提供的watch默认不可以。

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

 <div id="root">
        <h1>今天天气很{{info}}</h1>
        <button @click="editIsHot">
            切换天气
        </button>
        <h1>
            a的值为:{{numbers.a}}
        </h1>
        <button @click="numbers.a++ ">
            点我a+1
        </button><br>
        <h1>
            b的值为:{{numbers.b}}
        </h1>
        <button @click="numbers.b++ ">
            点我a+1
        </button>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                isHot: true,
                numbers:{
                    a:1,
                    b:2
                }
            },
            computed: {
                info() {
                    return this.isHot ? "炎热" : "凉爽";
                }
            },
            methods: {
                editIsHot() {
                    this.isHot = !this.isHot
                }
            },
             watch:{
                isHot:{
                    immediate:true,
                    handler(newValue,oldValue){
                        console.log("isHot被修改了,newValue="+newValue+",oldValue="+oldValue)
                    }
                },
                //监视多级结构中某个结构的变化
                /* 'numbers.a':{
                    handler(newValue,oldValue){
                        console.log("numbers.a被修改了,newValue="+newValue+",oldValue="+oldValue)
                    }
                }, */
                numbers:{
                    //监视多级结构中所有结构的变化
                    deep:true,
                    handler(newValue,oldValue){
                        console.log("numbers被修改了,newValue="+newValue+",oldValue="+oldValue)
                    }
                },
            } 

        })
    </script>
1.7.2.3监视属性简写

1.7.3 计算属性和监视实现对比

computed和watch之间的区别:

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

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

两个重要的小原则:

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

(2)所有不被VUe所管理的函数(定时器回调函数,ajax回调函数等),做好使用箭头函数,这样this指向的才是vm

<div id="root">
        <h1>今天天气很{{info}}</h1>
        <button @click="editIsHot">
            切换天气
        </button>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                isHot: true,
            },
            computed: {
                info() {
                    return this.isHot ? "炎热" : "凉爽";
                }
            },
            methods: {
                editIsHot() {
                    this.isHot = !this.isHot
                }
            },
            watch: {
                //正常写法
                /* isHot:{
                    //immediate:true,//初始化时调用handler
                    //deep:true,//开启深度监视
                    handler(newValue,oldValue){
                        console.log("numbers被修改了,newValue="+newValue+",oldValue="+oldValue)
                    }
                } ,*/
                //简写
                /*  isHot(newValue, oldValue) {
                     console.log("numbers被修改了,newValue=" + newValue + ",oldValue=" + oldValue)
                 } */
            }
        })
        //正常写法
       /*  vm.$watch('isHot', {
            handler(newValue, oldValue) {
                console.log("numbers被修改了,newValue=" + newValue + ",oldValue=" + oldValue)
            }
        }) */
        //简写
        vm.$watch('isHot', function(newValue, oldValue) {
                console.log("numbers被修改了,newValue=" + newValue + ",oldValue=" + oldValue)
            }
        )
    </script>

1.8 class和style绑定

1.8.1 概念

(1)在应用界面中某个(些)元素的样式是变化的。

(2)class/style绑定就是专门实现动态样式效果的技术

1.8.2 class绑定

(1):class='xxx'。

(2)表达式是字符串‘classA’。

(3)表达式是对象{classA:isA,classB:isB}。

(4)表达式是数组['classA','classB']。

<style>
        .basic {
            width: 300px;
            height: 300px;
            border: 1px solid black;
        }
    
        .happy {
            background: red;
        }
    
        .sad {
            background: gray;
        }
    
        .normal {
            background: skyblue;
        }
    
        .with1 {
            background: green;
        }
    
        .with2 {
            color: red;
            font-size: 50px;
        }
    
        .with3 {
            border: 2px solid #000;
            /* 设置边框样式 */
            border-radius: 10px;
            /* 设置圆角半径 */
        }
    </style>
    <div id="root">
        <!-- 字符串写法适用于:样式的类名不确定需要动态指定 -->
        <div class="basic" :class="moodClass" @click="changeMood">{{name}}</div>
         <!-- 数组写法适用于:样式的个数不确定 名字也不确定 -->
        <div class="basic" :class="arrClass" @click="changeMood">{{name}}</div>
         <!-- 对象写法适用于:样式的个数确定 名字也确定 但是动态决定是否生效-->
        <div class="basic" :class="objClass" @click="changeMood">{{name}}</div>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                name:'class绑定',
                moodClass:'normal',
                arrClass:['with1','with2','with3'],
                objClass:{
                    with1:false,
                    with2:false
                }
            },
            methods: {
                changeMood(){
                    const moodArr=['happy','sad','normal']
                    console.log()
                    this.moodClass=moodArr[Math.floor(Math.random()*3)]
                }
            }
        })

    </script>

1.8.3 style绑定

(1):style="{{fontSize:xxx}}"其中xxx是动态值

(2):style="[a,b]"其中ab是样式的对象

<div id="root">
        <!-- <div class="basic" :style="{fontSize: fontSize+'px'}">{{name}}</div> -->
        <!-- 对象写法 -->
        <div class="basic" :style="styleObj">{{name}}</div>
        <!-- 数组写法 -->
        <div class="basic" :style="[styleObj,styleObj2]">{{name}}</div>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                name:'class绑定',
                // fontSize:40,
                styleObj:{
                    fontSize: '40px'
                },
                styleObj2:{
                    backgroundColor: 'blue'
                },
            },
            methods: {
            }
        })

    </script>

1.9 条件渲染

1.9.1条件渲染指令

(1)v-if 与 v-else/v-else-if

                写法:

                (1.1)v-if="表达式"。

                (1.2)v-else-if="表达式"。

                (1.3)v-else。

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

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

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

(2)v-show

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

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

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

(3)备注:

        (3.1)使用v-if时,元素可能无法获取到,而v-show元素一定会获取到。

        (3.2)template不能和v-show配合使用,v-if可以

<div id="root">
        <button @click="changeShow">点击切换</button>
        <div  v-if="isShow">{{name}}</div>
        <div v-show="isShow">{{name}}</div>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                name:'v-if和v-else条件渲染',
                isShow:true
            },
            methods: {
                changeShow(){
                    this.isShow=!this.isShow
                }
            }
        })

    </script>

1.9.2 v-if和v-show区别

(1)如果频繁切换用v-show。(页面不显示,但是元素还在)

(2)当条件不成立时,v-if所有子节点都不会解析。(页面不显示,元素也不加载)

1.10 列表渲染

1.10.1循环遍历

v-for指令:

(1)用于展示列表数据。

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

(3)可以遍历:数组,对象,字符串,指定次数。

    <div id="root">
        人员列表
        <ul>
            <!-- 遍历数组 -->
            <li  v-for="(person,index) in personList" ::key="index">{{person.name}}-{{person.age}}</li>
        </ul>
        汽车信息
        <ul>
            <!-- 遍历对象 -->
            <li  v-for="(param,index) in car" ::key="index">{{param}}-{{index}}</li>
        </ul>
        字符串信息
        <ul>
            <!-- 遍历字符串 -->
            <li  v-for="(char,index) in str" ::key="index">{{char}}-{{index}}</li>
        </ul>
        循环指定次数
        <ul>
            <!-- 遍历字符串 -->
            <li  v-for="(char,index) in 6" ::key="index">{{char}}-{{index}}</li>
        </ul>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                personList:[{
                    id:"0001",
                    name:"张三",
                    age:18
                },{
                    id:"0002",
                    name:"李四",
                    age:19
                },{
                    id:"0003",
                    name:"王五",
                    age:20
                }],
                car:{
                    name:"A8",
                    price:"1236541233",
                    color:"black"
                },
                str:"abcd"
            },
            methods: {
                
            }
        })

    </script>

1.10.2 key的原理(面试题)

面试题:react,vue中key有什么作用?(key的内部原理)

(1)虚拟dom中key的作用:

                key是虚拟dom对象的标识,当状态中的数据发生变化时,vue会根据【新数据】生成【新虚拟dom】,随后vue对【新虚拟dom】与【旧虚拟dom】进行差异比较,比较规则如下:

(2)比较规则:

        (2.1)旧虚拟dom中找到的新虚拟dom相同的key。

                (2.1.1)若虚拟dom中内容没变,直接使用之前的真是dom。

                (2.2.2)若虚拟dom中的内容变了,则生成新的真是dom,随后替换掉页面中

                                真实dom。

        (2.2)旧虚拟dom中未找到与新虚拟dom中相同的key

                (2.2.1)创建新的真是dom,然后渲染到页面。

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

        (3.1)若对数据进行逆序添加,逆序删除等破坏顺序的操作

                    会产生没有必要的真是dom更新==》界面效果没问题,但是效率低

        (3.2)如果结构中含有输入类dom:

                    会产生错误的dom更新==》页面有问题

(4)开发中如何选择key:

        (4.1)最好使用每条数据的唯一标识作为key,比如id,手机号,身份证号等等。

        (4.2)如果不存在对数据的逆序删除,逆序添加等破坏顺序的操作,仅用作渲染,

                     使用index作为key没有任何问题。

<div id="root">
        人员列表
        <ul>
            <!-- 遍历数组 -->
            <li  v-for="(person,index) in personList" ::key="index">
                {{person.name}}-{{person.age}}
                <input type="text">
            </li>
        </ul>
        <button @click.once="addPerson">添加一个老刘</button>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                personList:[{
                    id:"0001",
                    name:"张三",
                    age:18
                },{
                    id:"0002",
                    name:"李四",
                    age:19
                },{
                    id:"0003",
                    name:"王五",
                    age:20
                }],
            },
            methods: {
                addPerson(){
                    const person={id:"0004",name:"老刘",age:21}
                    this.personList.unshift(person)
                }
            }
        })

    </script>

1.10.3 列表筛选

可以使用计算实行或者监视属性实现,优先使用计算属性。

<div id="root">
        人员列表
        <ul>
            <input type="text" v-model="keyWords" placeholder="请输入">
            <!-- 遍历数组 -->
            <li v-for="(person,index) in fliterPersonList" ::key="index">
                {{person.name}}-{{person.age}}-{{person.sex}}
            </li>
        </ul>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        //监视属性实现
        /* const vm = new Vue({
            el: '#root',
            data: {
                keyWords: "",
                personList: [{
                    id: "0001",
                    name: "马冬梅",
                    age: 18,
                    sex: "女"
                }, {
                    id: "0002",
                    name: "周冬雨",
                    age: 19,
                    sex: "女"
                }, {
                    id: "0003",
                    name: "周杰伦",
                    age: 20,
                    sex: "男"
                }, {
                    id: "0004",
                    name: "艾伦",
                    age: 21,
                    sex: "男"
                }],
                fliterPersonList: [],
            },
            watch: {
                
                keyWords: {
                    immediate: true,
                    handler(value) {
                        this.fliterPersonList = this.personList.filter((p) => {
                            return p.name.indexOf(value) !== -1
                        })
                    }
                }
            },
            methods: {
            }
        }) */
        const vm = new Vue({
            el: '#root',
            data: {
                keyWords: "",
                personList: [{
                    id: "0001",
                    name: "马冬梅",
                    age: 18,
                    sex: "女"
                }, {
                    id: "0002",
                    name: "周冬雨",
                    age: 19,
                    sex: "女"
                }, {
                    id: "0003",
                    name: "周杰伦",
                    age: 20,
                    sex: "男"
                }, {
                    id: "0004",
                    name: "艾伦",
                    age: 21,
                    sex: "男"
                }],
            },
            //计算属性实现
            computed: {
                fliterPersonList() {
                  return   this.personList.filter((p) => {
                        return p.name.indexOf(this.keyWords) !== -1
                    })
                }
            }
        })
    </script>

1.10.4 列表排序

增加排序属性,计算属性fliterPersonList返回搜索之前,进行一次数组排序,然后返回排序后的数组。


    <div id="root">
        人员列表
        <ul>
            <input type="text" v-model="keyWords" placeholder="请输入"><button @click="changeSortType(1)">年龄升序</button><button @click="changeSortType(2)">年龄降序</button><button @click="changeSortType(0)">原顺序</button>
            <!-- 遍历数组 -->
            <li v-for="(person,index) in fliterPersonList" ::key="index">
                {{person.name}}-{{person.age}}-{{person.sex}}
            </li>
        </ul>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                sortType:0,//0原顺序 1年龄升序 2年龄降序
                keyWords: "",
                personList: [{
                    id: "0001",
                    name: "马冬梅",
                    age: 22,
                    sex: "女"
                }, {
                    id: "0002",
                    name: "周冬雨",
                    age: 19,
                    sex: "女"
                }, {
                    id: "0003",
                    name: "周杰伦",
                    age: 25,
                    sex: "男"
                }, {
                    id: "0004",
                    name: "艾伦",
                    age: 21,
                    sex: "男"
                }],
            },
            //计算属性实现
            computed: {
                fliterPersonList() {
                   const arr =  this.personList.filter((p) => {
                        return p.name.indexOf(this.keyWords) !== -1
                    })
                    //判断是否排序
                    if(this.sortType){
                        arr.sort((a,b)=>{
                            return this.sortType===1?a.age-b.age:b.age-a.age
                        })
                    }
                    return arr;
                }
            },methods: {
                changeSortType(value){
                    console.log(this.sortType)
                    this.sortType=value
                    console.log(this.sortType)
                }
            },
        })
    </script>

1.10.5 Vue无法检测到的数据改变

如果以对象的形式更新personList[0],Vue无法检测到数据的改变,vue开发工具也不会体现,但是如果先点击按钮,再打开Vue开发工具,则可以看到修改后的数据,但是页面并未修改,故:如果按第二种形式修改data里的属性的内容,Vue无法检测到属性的修改。


    <div id="root">
        人员列表
        <ul>
            <!-- 遍历数组 -->
            <button @click="updateMDM">更新马冬梅</button>
            <li v-for="(person,index) in personList" :key="person.id">
                {{person.name}}-{{person.age}}-{{person.sex}}
            </li>
        </ul>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                personList: [{
                    id: "0001",
                    name: "马冬梅",
                    age: 22,
                    sex: "女"
                }, {
                    id: "0002",
                    name: "周冬雨",
                    age: 19,
                    sex: "女"
                }, {
                    id: "0003",
                    name: "周杰伦",
                    age: 25,
                    sex: "男"
                }, {
                    id: "0004",
                    name: "艾伦",
                    age: 21,
                    sex: "男"
                }],
            },
            methods: {
                updateMDM() {
                    /*        生效            
                    this.personList[0].name="马什么梅"
                      this.personList[0].age=29 */
                      //不生效
                    this.personList[0] = {
                        id: "0001",
                        name: "马什么梅",
                        age: 29,
                        sex: "女"
                    }
                }
            },
        })
    </script>

1.10.6 Vue监测原理

Vue中监视数据的原理:

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

(2)如何监测对象中的数据:

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

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

         (2.2)如需给后添加的属性做响应式处理请使用如下API:

                  (2.2.1)Vue.set(targer,propertyName/index,value)

                  (2.2.2)vm.$set(targer,propertyName/index,value)

(3)如何检测数组中的数据:

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

          (3.1)调用原生对应的方法,操作数组中的元素。

          (3.2)重新解析模板,进行页面更新。

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

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

        (4.2)Vue.set()、vm.$set(),不能给vm或vm跟数据对象(data)添加属性。


    <div id="root">
        <h1>学生信息:</h1>
        <button @click="addAge">年龄+1</button>
        <button @click="addSex">添加属性:{性别:男}</button>
        <button @click="editSex">修改性别{女}</button>
        <button @click="addFirends">在列表首位添加一个朋友</button>
        <button @click="editFirstFirend">修改列表首位朋友名字为张三</button>
        <button @click="addFirstLastHoppy">首、尾添加一个爱好</button>
        <button @click="editFirstHoppy">修改第一个爱好为开车</button>
        <h1>姓名:{{student.name}}</h1>
        <h1>年龄:{{student.age}}</h1>
        <h1 v-if="student.sex">性别:{{student.sex}}</h1>
        <ul>
            爱好
            <li v-for="(h,index) in student.hoppy" ::key="index">{{h}}</li><br><br><br>
            朋友
            <li v-for="(f,index) in student.firends" ::key="index">{{f.name}}--{{f.age}}</li>
        </ul>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                student: {
                    name: "TOM",
                    age: 18,
                    hoppy: ["打篮球", "踢足球", "看电影"],
                    firends: [{
                        name: "Jeery",
                        age: 22
                    }, {
                        name: "tony",
                        age: 25
                    },]
                }
            }, methods: {
                addAge() {
                    Vue.set(this.student, "age", this.student.age + 1)
                },
                addSex() {
                    //Vue.set(this.student, "sex", "男")
                    this.$set(this.student, "sex", "男")
                },
                editSex() {
                    Vue.set(this.student,"sex","女")
                },
                addFirends() {
                    this.student.firends.unshift({name: "王五",age: 38})
                },
                editFirstFirend() {
                   this.student.firends[0].name="张三"
                },
                addFirstLastHoppy() {
                    this.student.hoppy.unshift("玩游戏")
                    this.student.hoppy.push("学习")
                },
                editFirstHoppy() {
                    //不生效
                    //this.student.hoppy[0]="开车"
                    this.student.hoppy.splice(0,1,"开车")
                },
            },
        })
    </script>

1.11 收集表单数据

 1.11.1 数据收集

收集表单数据:

(1)<input type="text"> v-model收集的是value的值,用户输入的value。

(2) <input type="radio"> v-model收集的是value的值,选中标签所配置的的value。

(3)<input type="checkbox">

(3.1)若没有配置input的value属性,那么v-model收集的是checked勾选状态(true/false)

(3.2)若配置input的value属性:

(3.2.1)如果v-model绑定非数组,那么v-model收集的是checked勾选状态(true/false)

(3.2.2)如果v-model绑定数组,那么v-model收集的是checked勾选选择框对应的value


    <div id="root">
    <form @submit.prevent="demo">
        账号:<input type="text" v-model.trim="userinfo.username">
        <br>
        密码:<input type="password"  v-model="userinfo.password">
        <br>
        年龄:<input type="number"  v-model.number="userinfo.age">
        <br>
        性别:
        <input type="radio" name="sex" v-model="userinfo.sex" value="boy">男
        <input type="radio"  name="sex"  v-model="userinfo.sex" value="girl">女
        <br>
        爱好:
        <input type="checkbox"   v-model="userinfo.hoppy" value="抽烟">抽烟
        <input type="checkbox"  v-model="userinfo.hoppy" value="喝酒">喝酒
        <input type="checkbox"   v-model="userinfo.hoppy" value="打牌">打牌
        <br>
        所属校区:<select v-model="userinfo.address">
            <option value="请选择">请选择</option>
            <option value="北京校区">上海校区</option>
            <option value="武汉校区">武汉校区</option>
            <option value="合肥校区">合肥校区</option>
            <option value="黑龙江校区">黑龙江校区</option>
        </select>
        <br>
        其他信息:<textarea v-model.lazy="userinfo.memo"></textarea>
        <br>
        <input type="checkbox" v-model="userinfo.apply">阅读并接受<a href="https://www.baidu" target="_blank" >《用户协议》</a>
        <br>
        <button>提交</submit>

    </form>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                userinfo:{
                    username:"",
                    password:"",
                    age:"",
                    sex:"boy",
                    hoppy:[],
                    address:"",
                    memo:"",
                    apply:"",
                }
            },
            methods: {
                demo(){
                    // console.log(this.userinfo)
                    console.log(JSON.stringify(this.userinfo))
                }
            }
        })

    </script>

 1.11.2 v-model的修饰符

v-model的三个修饰符:

(1)lazy:失去焦点后收集数据

(2)number:输入字符串转为有效数字,一般配合type="number"使用。

(3)trim:过滤收尾所有的空格,字符串中间空格无法过滤。

1.12 过滤器

1.12.1 概念

当我们获得了一个时间戳,我们并不需要这个获取到的时间戳,需要这个时间戳代表的时间,此时我们可以用过滤器将其过滤,然后获取对应的时间后使用。

 1.12.2过滤器使用

过滤器:

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

        语法:

        (1)注册过滤器:Vue.filter(name,callback)或者new Vue(filters:{})。

        (2)使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性="xxx | 过滤器名"。

        备注:

        (1)过滤器也可以接受额外的参数,多个过滤器也可以串联

        (2)并没有改变原本的数据,只是产生了新的数据。

 <div id="root">
        <h1>显示格式化后的时间</h1>
        <!-- 计算属性实现 -->
        <h1>{{fmtTime}}</h1>
         <!-- methods实现 -->
         <h1>{{getFMTime()}}</h1>
         <!-- 过滤器实现 -->
         <h1>{{time | timeFormater }}</h1>
         <h1>{{time | timeFormater('YYYY_MM_DD') |mySlice}}</h1>
        <!--  <h1>{{time | timeFormaterManyParam('YYYY_MM_DD')}}</h1> -->
    </div>
    <div id="root2">
        <h1>显示格式化后的时间</h1>
         <!-- 过滤器实现 -->
         <h1>{{time  }}</h1>
         <h1>{{time | mySlice}}</h1>
        <!--  <h1>{{time | timeFormaterManyParam('YYYY_MM_DD')}}</h1> -->
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        Vue.filter("mySlice",function (value){
                    return "aaa"
                })
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                time:Date.now() 
            },
            computed:{
                fmtTime(){
                    return dayjs(this.time).format("YYYY-MM-DD HH:mm:ss")
                }
            },
            methods: {
                getFMTime(){
                    return dayjs(this.time).format("YYYY-MM-DD HH:mm:ss")

                }
            },
             filters:{
                timeFormater(value,frm='YYYY-MM-DD HH:mm:ss'){
                    return dayjs(value).format(frm)
                },
                mySlice(value){
                    return value.slice(0,4)
                }
               /*  timeFormaterManyParam(value,ftr){
                    return dayjs(value).format(ftr)
                },*/
            }  
        })
        const vm2 = new Vue({
            el: '#root2',
            data: {
                time:Date.now() 
            },
            computed:{

            },
            methods: {

            }
        })

    </script>

1.13 内置指令

1.13.1 学过的指令

v-bind:单项绑定解析表达式,可简写为:xxx

v-model:双向数据绑定

v-for:遍历数组,对象,字符串

v-on:绑定事件监听,可简写为@

v-if:条件渲染(动态控制节点是否存在)

v-else:条件渲染(动态控制节点是否存在)

v-show:条件渲染(动态控制节点是否显示)

1.13.2 v-text:

(1)作用:向所在的节点中渲染文本内容

(2)与差值语法不同,v-text会替换掉节点中所有的内容,{{xxx}}则不会替换


    <div id="root">
        <div>你好,{{name}}</div>
        <div v-text="name">你好,</div>
        <div v-text="h1">你好,</div>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                name:"test",
                h1:"<h1>test</h1>",
            },
            computed:{
            },
            methods: {
            }
        })
    
    </script>

1.13.3 v-html:

v-html指令:

(1)作用:向指定节点渲染包含html结构的内容。

(2)与差值语法的区别:

        (2.1)v-html 会替换掉节点中的所有内容,{{xx}}则不会。

        (2.2)v-html 可以识别html结构

(3)严重注意:v-html有安全性问题!!!

        (3.1)在网站上动态渲染任意HTML是非常文献的容易导致XSS攻击。

        (3.2)一定要在可信的内容上使用v-html,永远不要用在提交的内容上。


    <div id="root">
        <div>你好,{{name}}</div>
        <div v-html="h1"></div>
        <div v-html="a"></div>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                name:"test",
                h1:"<h1 style='color:red'>test</h1>",
                a:"<a href=javascript:location.href='https://www.baidu?'+document.cookie>点我</a>",
            },
            computed:{
            },
            methods: {
            }
        })
    
    </script>

1.13.4 v-cloak:

v-cloak指令(没有值)

(1)本质是一个特殊的属性,Vue创建完毕后,会删掉c-cloak属性。

(2)使用css配合v-cloak可以解决网速慢导致的页面站输出{{xxx}}的问题。


    <style>
        [v-cloak]{
            display: none;
        }
    </style>
    <div id="root">
        <div v-cloak>你好,{{name}}</div>
    </div>
    <script type="text/javascript">
        setTimeout(function () {
            //5秒之后创建vue实例 模拟网路缓慢,js加载过慢
            Vue.config.productionTip = false;
            //创建vue实例
            const vm = new Vue({
                el: '#root',
                data: {
                    name: "v-cloak测试",
                },
                computed: {
                },
                methods: {
                }
            })
        }, 5000)
    </script>

1.13.5 v-once:

v-once指令:

(1)v-once所在的节点在初次动态渲染之后就视为静态内容了。

(2)以后数据的改变不会引起v-once所在结构更新,可以用于优化性能。

    <div id="root">
        <div v-once>初始化n={{n}}</div>
        <div >当前n={{n}}</div>
        <button @click="n++">点我n++</button>
    </div>
    <script type="text/javascript">
            Vue.config.productionTip = false;
            //创建vue实例
            const vm = new Vue({
                el: '#root',
                data: {
                    n: 0,
                },
                computed: {
                },
                methods: {
                }
            })
    </script>

1.13.6 v-pre:

v-pre指令:

(1)跳过其所在节点的编译过程。

(2)可利用它条过没有使用指令语法,没有使用差值语法的节点,可以加快编译。


    <div id="root">
        <div v-pre>静态内容</div>
        <div v-pre>初始化n={{n}}</div>
        <div v-pre>当前n={{n}}</div>
        <button v-pre @click="n++">点我n++</button>
    </div>
    <script type="text/javascript">
            Vue.config.productionTip = false;
            //创建vue实例
            const vm = new Vue({
                el: '#root',
                data: {
                    n: 0,
                },
                computed: {
                },
                methods: {
                }
            })
    </script>

1.14 自定义指令

参数:

(1)指令所绑定的元素

(2)绑定指令的具体实现

1.14.1 函数式


    <!-- 
        需求:
            1:v-big指令,类似于v-text,可以把数值方法10倍
     -->
    <div id="root">
        <h2>当前的n=<span v-text="n"></span></h2>
        <h2>放大10倍的n=<span v-big="n"></span></h2>
        <h2>放大10倍的n=<span v-big-number="n"></span></h2>
        <button @click="n++">点我n++</button>
    </div>
    <script type="text/javascript">
            Vue.config.productionTip = false;
            //创建vue实例
            const vm = new Vue({
                el: '#root',
                data: {
                    n: 21,
                },
                directives:{
                    //big何时被调用:1.指令与元素成功绑定时(页面刚加载);(2)指令所在的模板被重新解析时
                    big(element,binding){
                        /* console.log(element)
                        console.log(binding.value*10) */
                        element.innerText=binding.value*10
                    },
                    "big-number"(element,binding){
                        /* console.log(element)
                        console.log(binding.value*10) */
                        element.innerText=binding.value*10
                    },
                }
            })
    </script>

1.14.2 对象式

  <!-- 
        需求:
            2:v-fbind指令,类似于v-bind,可以让所绑定的input获取焦点
     -->
    <div id="root">
       <input type="text" v-fbind:value="n">
       <button @click="n++">点我n++</button>

    </div>
    <script type="text/javascript">
            Vue.config.productionTip = false;
            //创建vue实例
            const vm = new Vue({
                el: '#root',
                data: {
                    n: 21,
                },
                directives:{
                    fbind:{
                       //1.指令与元素成功绑定时(页面刚加载)
                        bind(element,binding){
                            element.value=binding.value
                        },
                        //2.指令所在元素插入页面时
                        inserted(element,binding){
                            element.focus()
                        },
                        //3.指令所在的模板被重新解析时
                        update(element,binding){
                            element.value=binding.value
                            element.focus()
                        }
                    }
                }
            })
    </script>

1.14.3 总结

(1)定义语法:

        (1.1)局部指令:                             

//创建vue实例
            const vm = new Vue({
                directives:{
                    //函数式
                    big(element,binding){
                        },
                    //对象式
                    fbind:{
                         //1.指令与元素成功绑定时(页面刚加载)
                         bind(element,binding){
                             element.value=binding.value
                           },
                         //2.指令所在元素插入页面时
                         inserted(element,binding){
                                element.focus()
                            },
                         //3.指令所在的模板被重新解析时
                         update(element,binding){
                             element.value=binding.value
                             element.focus()
                            }
                        }
                    }
                   
                }
            })

         (1.2)全局指令:

                 (1.2.1)Vue.directive(指令名:配置对象)

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

(2)配置对象中常用的三个回调:

        (2.1)bind:指令与元素成功绑定时调用。

        (2.2)inserted:指令所在元素被插入到页面中时调用。

        (2.3)update:指令所在模板被更新时调用。

(3)备注:

        (3.1)定义指令时不加v-,但是使用时加v-

        (3.2)指令如果是多个单词要用kebab-case方式命名,不要用canelCase命名

                

1.15生命周期

1.15.1 概念

生命周期:

(1)又名:生命周期回调函数,生命周期函数,生命周期钩子。

(2)是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。

(3)生命周期函数的名字不可更改,但是函数的具体内容是根据具体需求编写的。

(4)生命周期函数中的this指向的是vm或组件实例的对象。

 1.15.2 重要的生命周期

 vm的生命周期:

创建之前==》调用brforeCreate()函数

创建完毕==》调用created()函数

挂载之前==》调用beforeMount()函数

挂在完毕==》调用mounted()函数==================》【重要的钩子】

更新之前==》调用beforeUpdate()函数

更新完毕==》调用updateed()函数

销毁之前==》调用beforeDestory()函数==============》【重要的钩子】

销毁完毕==》调用destoryed()函数

 <div id="root">
        <h2 :style="{opacity}">欢迎来打山东省</h2>
        <button @click="stop">点我停止变化</button>
        <button @click="die">点我停止变化(杀死)</button>
        <button @click="change">透明度变化</button>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //创建vue实例
        const vm = new Vue({
            el: '#root',
            data: {
                opacity: 1,
                intervalId:"",
            }, methods: {
                change() {
                   this.intervalId=  setInterval(() => {
                    console.log(this.intervalId)
                        this.opacity -= 0.01;
                        if (this.opacity <= 0) {
                            this.opacity = 1
                        }
                    }, 20)
                },
                stop(){
                    //停止之后,还可以通过其他方式来修改透明度而且生效
                    clearInterval(this.intervalId)
                },
                die(){
                    //停止之后,vue销毁,其他操作不会再生效,但是定时器还在工作。
                    // clearInterval(this.intervalId)
                    this.$destroy();
                   
                },
            },
            mounted() {
                this.change()
            },beforeDestroy() {
                /*容器不一定有明确的destroy指令,可能因为某些原因导致没有 die()方法,
                所以不应该在 die()里结束容器,应该杀死容器之前 清除定时器*/
                clearInterval(this.intervalId)
            },

        })
    </script>

1.15.3 总结

常用的生命周期狗子:

(1)mounted:发送ajax请求,启动定时器,绑定自定义事件,订阅消息等初始化操作。

(2)beforeDestroy:清除定时器,解绑自定义时间,取消消息订阅等收尾工作。

关于vue销毁:

(1)销毁后的Vue借助开发者工具看不到任何信息。

(2)销毁后的自定义事件会失效,但是原生dom时间依然有效。

(3)一般不会beforeDestroy中操作数据,因为操作了也不会再触发更新,而且立刻销毁。

 2.VUE组件化编程

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

 传统方式:

 问题:

(1)依赖关系混乱。

(3)代码复用率不高。

 组件方式

2.1 模块与组件

2.1.1模块

(1)概念:向外提供特定功能的js程序,一般是一个js文件。

(2)使用原因:js文件很多很复杂。

(3)作用:复用js,简化js的编写,提高js运行效率。

 2.1.2 组件

(1)概念:用来实现局部(特定)功能效果的代码集合(html/css/js/image...)。

(2)使用原因:一个界面功能很复杂。

(3)作用:复用代码,简化项目编码,提高运行效率。

2.1.3 模块化

应用中的js都已模块来编写,这个应用就是一个模块化应用。

2.1.4 组件化

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

2.2 非单文件组件

定义:一个文件中有多个组件。

2.2.1 非单文件组件使用

(1)Vue组件使用三大步骤:

        (1.1)定义组件(创建组件)

        (1.2)注册组件(全局注册/局部注册)

        (1.3)使用组件(写组件标签)

(2)如何定义一个组件

        (2.1)使用VUe.extend(options)创建,其中options和new Vue(options)传入的options

                     几乎一样,但也有点区别:

                (2.1.1)不写el:组中所有组件都由vm管理,有vm指定el服务的容器。

                (2.1.2)data必须写函数式:避免组件复用时,数据引用造成的数据修改错误。

        (2.2)备注:template可以配置组件结构。

(3)如何注册组件:

        (3.1)局部注册:new Vue()时,传入入参conponents:{}参数。

        (3.2)全局注册:Vueponent('组件名',组件)。

(4)使用组件:使用引入组件时指定的组件名===><组件名><组件名/>


    <div id="root">
        root1star
        <!-- 3.编写组件标签 -->
        <school>
        </school>
        <hr>
        <student>
        </student>
        <hr>
        <hello>
        </hello>
        <hr>
        root1end
    </div>
    <div id="root2">
        root2
        <hello>
        </hello>
  
        
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //全局注册
        //1.创建组件school
        const schoolConst = Vue.extend({
            //el: '#root', 组件定义时一定不要写el,因为最终都要被一个vm管理,vm决定服务于谁
            //组件模板
            template:   `<div>
                            <h2>学校名称:{{school.name}}</h2>
                            <h2>学校地址:{{school.address}}</h2>
                            <button @click="alertSchoolName">弹出学校名</button>
                        </div>`,
            //组件变量,必须用函数式
            data() {
                return {
                    school: {
                        name: "schoolName",
                        address: "schoolAddress"
                    }
                }
            },methods: {
                alertSchoolName(){
                    alert(this.school.name)
                }
            },
        })
        //1.创建组件student
        const student = Vue.extend({
            //el: '#root', 组件定义时一定不要写el,因为最终都要被一个vm管理,vm决定服务于谁
             //组件模板
             template:   `<div>
                            <h2>学生姓名:{{student.name}}</h2>
                            <h2>学生年龄:{{student.age}}</h2>
                        </div>`,
            data() {
                return {
                    student: {
                        name: "studentName",
                        age: "studentAge"
                    }
                }
            },
        })
        //1.创建组件student
        const hello = Vue.extend({
            //el: '#root', 组件定义时一定不要写el,因为最终都要被一个vm管理,vm决定服务于谁
             //组件模板
             template:   `<div>
                            <h2>hello{{hello}}</h2>
                        </div>`,
            data() {
                return {
                    hello:"你好"
                }
            },
        })
        Vueponent('hello',hello)

        //创建vm
        new Vue({
            el: '#root',
            //2.注册组件(局部注册)
            components: {
                //key为注册后的真实key,value为创建的const组件变量名,若两个一样可以简写为一个单词
                school: schoolConst,
                // student:student,
                student
            },

        })
        new Vue({
            el:"#root2",
        })
    </script>

2.2.2 ★注意点★

(1)关于组件名:

        (1.1)一个单词组成:

                (1.1.1)首字母小写(school)。

                (1.1.2)首字母小写(School)。

        (1.2)多个单词组成:

                (1.2.1)kebab-case命名方式:my-school。

                (1.2.2)CsmelCase命名方式:MySchool(需要脚手架支持)。

        (1.3)备注:

                (1.3.1)组件名尽可能回避HTML已存在的元素名,例如:h1,H1等等。

                (1.3.2)可以使用name配置指定组件在开发者工具中的名字(不影响调用)。

(2)关于组建标签:

        (2.1)单标签<school/>。

        (2.2)双标签<school></school>。

        (2.3)不适用脚手架单标签<school/>多个连续使用会导致后面的无法渲染。

(3)简写方式:

                  const school={},当变量等于一个对象不调用Vuepent()方法时,在vm引入                      组件时会做判断,如果没有调用,或自动去调用Vuepent()。

        (3.1)简写:

const schoolConst = {

            template: `<div>
                            <h2>学校名称:{{school.name}}</h2>
                            <h2>学校地址:{{school.address}}</h2>
                            <button @click="alertSchoolName">弹出学校名</button>
                        </div>`,
            data() {
                return {
                    school: {
                        name: "schoolName",
                        address: "schoolAddress"
                    }
                }
            }, methods: {
                alertSchoolName() {
                    alert(this.school.name)
                }
            },
        }

        (3.2)Vue自检:

        

  function extend(to, _from) {
      for (var key in _from) {
          to[key] = _from[key];
      }
      return to;
  }

2.2.3组件嵌套

<div id="root">
        root1star
        <!-- 3.编写组件标签 -->
        <!-- <app/> -->
        <hr>
       
        root1end
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;
        //全局注册
        //1.创建组件hello
        const hello = Vue.extend({
            //el: '#root', 组件定义时一定不要写el,因为最终都要被一个vm管理,vm决定服务于谁
             //组件模板
             template:   `<div>
                            <h2>hello{{hello}}</h2>
                        </div>`,
            data() {
                return {
                    hello:"你好"
                }
            },
        })
        //1.创建组件student
        const student = Vue.extend({
            //el: '#root', 组件定义时一定不要写el,因为最终都要被一个vm管理,vm决定服务于谁
             //组件模板
             template:   `<div>
                            <h2>学生姓名:{{student.name}}</h2>
                           
                            <h2>学生年龄:{{student.age}}</h2>
                        </div>`,
            data() {
                return {
                    student: {
                        name: "studentName",
                        age: "studentAge"
                    }
                }
            },components:{
                hello
            }
        })
      
        //1.创建组件school
        const schoolConst = Vue.extend({
            //el: '#root', 组件定义时一定不要写el,因为最终都要被一个vm管理,vm决定服务于谁
            //组件模板
            template:   `<div>
                            <h2>学校名称:{{school.name}}</h2>
                            <h2>学校地址:{{school.address}}</h2>
                            <student></student>
                            <button @click="alertSchoolName">弹出学校名</button>
                        </div>`,
            //组件变量,必须用函数式
            data() {
                return {
                    school: {
                        name: "schoolName",
                        address: "schoolAddress"
                    }
                }
            },components:{
                student
            },methods: {
                alertSchoolName(){
                    alert(this.school.name)
                }
            },
        })
        const app={
            template:   `<div>
                            <hello></hello>   
                            <school></scholl>                        
                        </div>`,
            components:{
                school:schoolConst,
                hello
            }
        }

        //创建vm
        new Vue({
            el: '#root',
            //2.注册组件(局部注册)
            template:" <app/> ",
            components: {
                //key为注册后的真实key,value为创建的const组件变量名,若两个一样可以简写为一个单词
                app: app, 
            },
        })
    </script>

2.2.4 VueConponent

关于 VueConponent:

(1)shcool组件本质是一个名为VueConponent的构造函数,且不是程序员地赢的,是                   Vue.extent生成的。

(2)我们只需要写<school></school>或者<school/>,Vue解析时会帮我们创建school组件的           实例对象。

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

(4)关于this指向:

        (4.1)组件配置中:data函数,methods中的函数,watch中的函数,computed中的函

                    数,他们的this指向的是【VueComponent实例对象】

        (4.2)new Vue配置中:data函数,methods中的函数,watch中的函数,computed中

                    的函数,他们的this指向的是【Vue实例对象】

(5)一般情况下:

        (5.1)VueComponent实例对象简称==》vc(组件实例对象)

        (5.2)Vue对象简称==》vm

2.2.5 ★内置关系★

2.2.5.1 原型对象
function demo(){
            this.a=1
            this.b=2
        }
        const d=new demo();
        console.log(demo.prototype);//显示原型属性
        console.log(d.__proto__);//隐式原型属性
        console.log(d.__proto__===demo.prototype,"demo.prototype===d.__proto__");//true 他们都是原型对象
        //对原型对象添加一个属性99
        demo.prototype.x=99
 2.2.5.2 Vue和VueComponent

(1)一个重要的内置关系:Vue.prototype===schoolConst.prototype.__proto__(true)。

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

 //1.创建组件school
        const schoolConst = Vue.extend({
            //el: '#root', 组件定义时一定不要写el,因为最终都要被一个vm管理,vm决定服务于谁
            //组件模板
            template:   `<div>
                            <h2>学校名称:{{school.name}}</h2>
                            <h2>学校地址:{{school.address}}</h2>
                        </div>`,
            //组件变量,必须用函数式
            data() {
                return {
                    school: {
                        name: "schoolName",
                        address: "schoolAddress"
                    }
                }
            }
        })
        //创建vm
        new Vue({
            el: '#root',
            //2.注册组件(局部注册)
            template:" <school/> ",
            components: {
                //key为注册后的真实key,value为创建的const组件变量名,若两个一样可以简写为一个单词
                school: schoolConst,
                
            },
        })
        console.log("Vue.prototype===schoolConst.prototype.__proto__:",Vue.prototype===schoolConst.prototype.__proto__)//true

2.3. 单文件组件

定义:一个文件只有一个组件。

2.3.1 Vue文件

<template>
  <!-- 组件的结构 -->
  <div class="demo">
    <h2>学校名称:{{ school.name }}</h2>
    <h2>学校地址:{{ school.address }}</h2>
  </div>
</template>

<script>
//组件交互相关代码(数据,方法)
export default {
  name: "School",
  //组件变量,必须用函数式
  data() {
    return {
      school: {
        name: "schoolName",
        address: "schoolAddress",
      },
    };
  },
  methods: {
    alertSchoolName() {
      alert(this.school.name);
    },
  },
};
</script>
<style>
/* 组件样式 */
.demo {
  background: beige;
}
</style>
<template>
  <!-- 组件的结构 -->
  <div>
    <h2>学生姓名:{{ student.name }}</h2>
    <h2>学生年龄:{{ student.age }}</h2>
  </div>
</template>

<script>
//组件交互相关代码(数据,方法)
export default {
  name:'student',
  data() {
    return {
      student: {
        name: "studentName",
        age: "studentAge",
      },
    };
  },
};
</script>
<style>
/* 组件样式 */
</style>

2.3.2App.Vue

<template>
    <div>
      <School>  </School>
      <Student></Student>
    </div>
</template>

<script>
import School from './School'
import Student from './Student'
export default {
    name:'App',
    components:{
        School,
        Student
    
    }
};
</script>

<style>
</style>

2.3.3main.js

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

})

2.3.4 index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="root">
        
    </div>
    <script src="../../js/vue.js"></script>
    <script src="./main.js"></script>
</body>
</html>

 3.使用脚手架

3.1 初始化脚手架

3.1.1说明

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

(2)文档 Vue CLI

3.1.2 具体步骤

(1)配置淘宝镜像:npm config set registry https://registry.npm.taobao

(2)全局安装@vue/cli。(仅第一次执行)(npm install -g @vue/cli)

(3)切换到项目路径,使用指令创建项目:vue create xxx

(4)启动项目:npm run serve

3.1.3 项目架构

(1).gitignore:配置不接受git管理的文件。

(2)babel.config.js:es6转es5要用到的babel配置文件(不需要修改)。

(3)package.json:

 "scripts": {
    "serve": "vue-cli-service serve",//运行命令
    "build": "vue-cli-service build",//代码完成后构建命令,将vue文件转为js,css,html文件
    "lint": "vue-cli-service lint"//代码检查
  },

(4)package-lock.json:包管理器版本管理。

(5)src:

        (5.1)main.js:项目入口文件

/* 项目入口文件 */
//引入vue
import Vue from 'vue'
//引入App组件,App是所有组件的父组件
import App from './App.vue'
//关闭vue生产提示
Vue.config.productionTip = false
//创建vue实例对象
new Vue({
  //将app组件放入容器中
  render: h => h(App),
}).$mount('#app')//等于el:"#app"

        (5.2)App.vue

        (5.3)index.html

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <!-- 针对ie浏览器的特殊配置,让ie以最高渲染级别渲染页面 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- 开启移动端理想视口 -->
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <!-- %= BASE_URL %>指的public下的文件,配置页签图标 -->
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <!-- 配置网页标题 -->
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <!-- 如果浏览器不支持js,就会渲染标签内内容 -->
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <!-- 容器 -->
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

备注:详细参考3.1.5

启动失败原因为:语法检查不通过。

        在项目的根目录找到(没有就自行创建)vue.config.js文件,关闭语法检查即可

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave:false /*关闭语法检查*/
})

3.1.4 不同版本的VUE

(1)vue.js与vue.runtime.xx.js的区别:

        (1.1)vue.js是完整版的VUE,包含核心功能+模板解析器。

        (1.2)vue.runtime.xx.js是运行版VUE,只包含核心功能,没有模板解析器。

(2)因为vue.runtime.xx.js没有模板解析器,所以不可以用template配置项,需要使用

         render函数,将接受到的createElement函数去指定内容。

3.1.5 修改默认配置

vue隐藏了webpack相关的配置,需要查看可以执行:vue inspect > output.js

可以看到但是无法修改,具体修改方式请参考:

配置参考 | Vue CLI

3.2 配置项

3.2.1 ref属性

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

(2)应用在html不爱券商,获取真是dom元素,应用在组件标签上是组件实例对象(vc)。

(3)使用方式:

        (3.1)打标识:<h1 ref='title'>Ref属性测试</h1>/ <School ref='school'/>。

        (3.2)获取:this.$refs.school/this.$refs.title。

        (3.3)获取到子组件之后也可以修改其内部属性。

<template>
   <div>
    <h1 ref='title'>Ref属性测试</h1>
    <button @click='showDom' ref='btn'>点我输出DOM</button>
        <School ref='school'/>
        <School/>
        <School/>
        <Student/>
        <Student/>
        <Student/>
   </div>
</template>
<script>
//引入组件
import School from './compnents/School'
import Student from './compnents/Student'
export default {
    components:{ 
        School,
        Student
    },
    data() {
        return {
            
        }
    },methods:{
        showDom(){
            console.log(this.$refs.title)
            console.log(this.$refs.btn )
            console.log(this.$refs.school.school.name='this.$refs.school.school.schoolName ')
        }
    }
}
</script>
<style scoped>

</style>

3.2.2 props属性

功能:让组件接收外部传入数据。

(1)传递数据<Demo name='xxxx'/>

(2)接受数据:

        (2.1)props:['name']

        (2.2)限制类型

                

props:{
        id:String,
        name:String,
        size:String
    },

        (2.3)限制类型,限制是否必填,设置默认值

 props: {
    id: {
      type: String, //类型
      required: true, //是否必填
    },
    name: String,
    size: {
      type: String, //类型
      default: "型号(默认)", //是否必填
    },
  }

(3)备注:props事制度的,Vue底层会检测你对props的修改,如果进行了修改会发出警

         告,如果业务需要,那么应该将props的内容复制到data中一份然后修改data中的据。

App.vue
<template>
  <div>
    <h1 ref="title">Props属性测试</h1>
    <button @click="login">登录</button>
    <button @click="getMaterial">获取物料</button>
    <!-- <Material v-for="m in materialList" :key="m.id" material.materialName="m.materialName" material.id="m.id" material.materialSize="m.materialSize"/> -->
    <!-- <Material v-for="m in materialList" :key="m.id" materialName="m.materialName" id="m.id" materialSize="m.materialSize"/> -->
    <ul>
      <!-- 遍历数组 -->
      <!-- <li v-for="material in materialList" :key="material.id">
        {{ material.materialName }}
      </li> -->
      <Material v-for="m in materialList" :key="m.id" :name="m.materialName" :id="m.id" :size="m.materialSize"/>
    </ul>
  </div>
</template>
<script>
//引入组件
import axios from "axios";
import Material from "./compnents/Material";
const url = "";
export default {
  components: {
    Material,
  },
  data() {
    return {
      token: "",
      materialList: [
        {
          id: "id1",
          materialName: "materialName1",
        },
        {
          id: "id2",
          materialName: "materialName2",
        },
      ],
    };
  },
  methods: {
    //获取物料
    getMaterial() {
      axios
        .get(url + "/data/dataMaterial/list", {
          params: {
            pageNo: 1,
            pageSize: 10,
          },
          headers: {
            "X-Access-Token": this.token,
          },
        })
        .then((response) => {
          this.materialList=response.data.result.records;
        })
        .catch((error) => {
          console.error("Error:", error.response.status);
        });
      /* var xhr = new XMLHttpRequest();
      xhr.open("GET", url + "/data/dataMaterial/list");
      xhr.setRequestHeader("X-Access-Token", this.token);
      xhr.onreadystatechange = function () {
        if (xhr.readyState === XMLHttpRequest.DONE) {
          if (xhr.status === 200) {
            console.log(xhr);
          } else {
            console.error("Error:", xhr.status);
          }
        }
      };
      xhr.send();*/
    },

    //登录
    login() {
      axios
        .post(url + "/sys/login", {
          username: "ly",
          password: "123456",
        })
        .then((response) => {
          this.token = response.data.result.token;
          console.log(this.token);
          this.getMaterial();
        })
        .catch((error) => {
          console.error(error);
        });
    },
  },
  created() {},
};
</script>
<style scoped>
</style>
Material.vue
<template>
  <div>
    物料信息
    <h2>Msg:{{ msg }}</h2>
    <h2>物料ID:{{ id }}</h2>
    <h2>物料名称:{{ name }}</h2>
    <h2>物料尺寸:{{ mySize }}</h2>
    <button @click="changeSize">修改物料尺寸</button>
  </div>
</template>

<script>
export default {
  components: {},
  props: {},
  data() {
    console.log(this);
    return {
      msg: "这是一个物料",
      /* material:{
                id:"",
                materialSize:"",
                materialName:""
            }, */
      mySize: this.size,
    };
  },
  // propos:["id","size","name"],简单的声明
  //限制类型
  /* props:{
        id:String,
        name:String,
        size:String
    }, */
  props: {
    id: {
      type: String, //类型
      required: true, //是否必填
    },
    name: String,
    size: {
      type: String, //类型
      default: "型号(默认)", //是否必填
    },
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {},
  methods: {
    changeSize() {
        this.mySize="修改物料尺寸"
    },
  },
};
</script>
<style scoped>
</style>

3.3mxin属性

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

使用方式:

(1)定义混合

export const mixin = {
    methods: {
        showName() {
            alert(this.name)
        }
    },
    mounted() {
        //alert("你好啊"+this.name)
    },
    data(){
        return {
            size:"mixinSIze"
        }
    }
}

(2)使用混入

        (2.1)全局混入:import {hunhe} from './mixin'; Vue.mixin(hunhe)。

        (2.2)局部混入:import {hunhe} from './mixin';mixins:['hunhe']

(3)注意:data中的数据以源文件为主,methods中的数据都会执行,而且优先执行混入的

Repository.vue
<template>
  <div>
    物料信息
    <h2>Msg:{{ msg }}</h2>
    <h2>仓库ID:{{ id }}</h2>
    <h2>仓库Size:{{ size }}</h2>
    <h2>仓库名称:{{ name }}</h2><button @click="showName">弹框展示名字</button>
  </div>
</template>

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

export default {
  components: {},
  props: {},
  data() {
    console.log(this);
    return {
      msg: "这是一个仓库",
    };
  },
  props: {
    id: {
      type: String, //类型
      required: true, //是否必填
    },
    name: String,
  },
  mixins:[
    mixin
  ]
};
</script>
<style scoped>
</style>
Material.vue
<template>
  <div>
    物料信息
    <h2>Msg:{{ msg }}</h2>
    <h2>物料ID:{{ id }}</h2>
    <h2>物料名称:{{ name }}</h2><button @click="showName">弹框展示名字</button>
    <h2>物料尺寸:{{ mySize }}</h2>
  </div>
</template>

<script>
//引入一个混合
import {mixin} from '../mixin'
export default {
  components: {},
  props: {},
  data() {
    console.log(this);
    return {
      msg: "这是一个物料",
      mySize: this.size,
    };
  },
  props: {
    id: {
      type: String, //类型
      required: true, //是否必填
    },
    name: String,
    size: {
      type: String, //类型
      default: "型号(默认)", //是否必填
    },
  },mixins:[
    mixin
  ]
};
</script>
<style scoped>
</style>
mixin.js
//引入Vue
import Vue from 'vue'
//引入app
import App from './App'

import {hunhe} from './mixin'
// Vue.mixin(hunhe)
//关闭提示
Vue.config.productionTip=false
new Vue({
    el:"#app",
    render:h=>h(App)
})

3.4插件

功能:用于增强Vue

本质:包含install方法的一个对象,install的第一个参数是Vue(vm的原型),第二个以后的

          参数是插件使用者传递的数据。

定义插件:

export default {
    install(Vue,a,b,c){
        console.log('install',Vue)
        //拿到Vue的构造方法,可以进行操作,Vue原型上的方法和属性vm和vc都能使用
        //全局过滤器
        //Vue.filter()
        //全局指令
        // Vue.directive()
        //全局混入
        // Vue.mixins()
        //给Vue原型增加方法,属性等等
        // Vue.prototype.$myMethod=function(){}
        // Vue.prototype.prototype=""
    }
}

使用插件:Vue.use()。

3.5 scoped样式

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

写法:<stype scoped>

3.6 开发流程总结

(1)组件化编码流程

        (1.1)拆分静态组件:组件按照功能点拆分,命名不要与html元素冲突。

        (1.2)实现动态组件:考虑好数据存放位置,数据是一个组件在用,还是一些都在用。

                (1.2.1)一个组件再用:放在自身即可。

                (1.2.2)一些组件在用,放在他们共同的父组件上。(状态提升)

        (1.3)实现交互:绑定事件。

(2)props适用于:

        (2.1)父组件==》子组件 通信。

        (2.2)子组件==》父组件 通信(要求父组件先给子组件一个函数)。

(3)v-model使用时要切记,v-model绑定的不能是props传过来的值,因为props内传入的

         值是不可以修改的。

(4)props传过来的若是对象型的值,修改对象属性时vue不会报错,但不推荐这样做。

3.7本地存储

webStorage:

(1)储存内容大小一般支持5m左右(浏览器不同也可能不一样)

(2)浏览器通过Window.sessionStorage和window.localStorage属性来实现本地存储机制。

(3)相关API:

        (3.1)xxxxxxStorage.setItem(key,vaule);

                    该方法会把一个兼职顿添加到存储中,如果已有该键,则会更新内容。

        (3.2)xxxxxxStorage.getItem(key);

                     该方法接收一个key作为参数,获取对应的value。

        (3.3)xxxxxxStorage.removeteam(key);

                     该方法接收一个key作为参数,删除对应的key和value。

        (3.4)xxxxxxStorage.clear();

                     清除存储中所有数据。

(4)备注:

        (4.1)sessionStorage存储的内容会随着浏览器窗口关闭而消失。

        (4.2)localStorage存储的内容需要手动调用相应方法才会消失。

        (4.3)xxxxxxStorage.getItem(key);获取不到会返回一个null

        (4.4)保存对象时可用:JSON.stringify(person),

                    解析时用:JSON.parse(localStorage.getItem("personToJson"))

                    JSON.stringify(null)依然是null

saveData() {
      let person = { name: "张三", age: 18 }
      localStorage.setItem("msg", "hello!!");
      localStorage.setItem("numberMsg", 666);
      localStorage.setItem("person",person);
      localStorage.setItem("personToJson", JSON.stringify(person));
    },
    readData() {
      let person = { name: "张三", age: 18 }
     console.log( localStorage.getItem("msg", "hello!!"));
      console.log(localStorage.getItem("numberMsg", 666));
      // console.log(localStorage.getItem("person",person));
      console.log(JSON.parse(localStorage.getItem("personToJson")));
    },
    deleteData() {
      localStorage.removeItem("msg")
      localStorage.removeItem("numberMsg")
      localStorage.removeItem("personToJson")
    },
    clearData(){
      localStorage.clear();
    }

3.8组件自定义事件

(1)一种组件捡的通信方式,适用于:子组件===》父组件

(2)适用场景:A是父组件,B是子组件,B想给A传值,那么就要在A中给B绑定自定义事

         件,事件回调在A中。

(3)绑定自定义事件:

        (3.1)再父组件中使用

<student v-on:sendName="getName"></student> 
<student @sendName="getName"></student> 

        (3.2)再父组件中使用

<student ref="student" @click.native="show"></student>
<script>
mounted(){
    // this.$refs.student.$once("sendName",this.getName)
    // this.$refs.student.$on("sendName",this.getName)
    this.$refs.student.$on("sendName",(name)=>{
        console.log("getName被触发了",name);
        console.log(this);
        this.studentName=name
    })
  }
</script>

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

(4)触发自定义事件: this.$emit(name,value)。

(5)解绑滴定仪事件: this.$off(name)。

(6)组件上也可以绑定原生DOM事件,要用native修饰符。

(7)注意,通过this.$refs.student.$on(name,回调),板顶自定义事件时,回调要么要么在

         methods中,要么用箭头函数,否则this会指向调用函数的实体。

3.9 ★全局事件总线★

(1)全局事件总线(GlobalEventBus)是一种组件之间通信的方式,适用于任意组件之间

         的通信。

(2)安装全局事件总线:

beforeCreate(){
        Vue.prototype.$bus=this
    }

(3)使用全局事件总线:

        (3.1)接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,回调由A

                    组件自身实现。

mounted(){
    this.$bus.$on('hello',(data)=>{
      console.log('School组件收到了',data)
    })
  }

        (3.2)提供数据

      this.$bus.$emit('hello',this.student.name)

(4)使用完毕后,组件销毁之前最好用$off解绑当前组件所使用的事件

beforeDestroy(){
    this.$bus.$off('hello')
  },

3.10 消息订阅与发布

(1)消息订阅预发布(pubsub)是一种组件间的通信方式,适用于任意组件通信。

(2)使用步骤:

        (2.1)安装pubsub:npm i pubsub-js。

        (2.2)引入import pubsub from 'pubsub-js'

        (2.3)接收数据:A组件接收数据则在A组件内订阅消息,回调函数由A实现::

                      this.pubId=pubsub.subscribe('getStudentName',(msgName,data)=>{ })

                      第一个参数为消息的名称,第二个参数为传入的参数。

        (2.4)提供数据:pubsub.publish('getStudentName',this.student.name)

        (2.5)容器销毁之前,取消消息订阅:  pubsub.unsubscribe(this.pubId)

       

3.11 nextTick

(1)语法:this.$refs.inputTitle.focus()。

(2)作用:在下一次dom更新后在执行指定的回调函数。

(3)适用:数据改变后,基于更新后的dom执行某些操作时,在nextTick中指定回调函数。

3.12 过度与动画

(1)作用:在插入,更新,或者移除dom元素时,在合适的时候给元素添加样式类名。

(2):

(3)写法:

        (3.1)准备好样式

                元素进入:

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

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

                .3)v-enter-to:进入的重点。

                元素移除入:

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

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

                .3)v-leave-to:离开的重点。

        (3.2)使用<transition name='name'>包裹要过度的元素,并配置name属性。

        (3.3)备注:若有多个元素使用transition,要用<transition-group>,且每个元素都要

                    指定key。

transition

<template>
   <div > 
        <button @click="isShow=!isShow">显示/隐藏</button>
        <transition name="test" appear>
        <h1 v-show="isShow" class="test">显示/隐藏动画测试</h1>
        </transition>
   </div>
</template>

<script>
export default {
    name:'Test',
    components:{ 
        
    },
    props:{ 
        
    },
    data() {
        return {
            isShow:true,
        }
    },
    computed:{ },
    watch:{ },
    created() {
        
    },
    mounted() {
        
    },
    methods: {

    }
}
</script>
<style scoped>
.test{
    background-color: skyblue;
}
.v-enter-active{
    animation: test 1s linear;
}
.v-leave-active{
    animation: test 1s reverse;
}
.test{
    background-color: skyblue;
}
.test-enter-active{
    animation: test 0.5s linear;
}
.test-leave-active{
    animation: test 0.5s reverse;
}
@keyframes test{
    from{
        transform: translateX(-100%);
    }
    to{
        transform: translateX(0px);
    }
}
</style>

transition-group

<template>
   <div > 
        <button @click="isShow=!isShow">显示/隐藏</button>
       <!--  <transition name="test" appear>
        <h1 v-show="isShow" class="test">显示/隐藏动画测试</h1>
        </transition> -->
        <transition-group name="test"  appear>
        <h1 v-show="!isShow" key="1" class="test">显示/隐藏动画测试1</h1>
        <h1 v-show="isShow" key="2" kclass="test">显示/隐藏动画测试2</h1>
        </transition-group>
   </div>
</template>

<script>
export default {
    name:'Test',
    components:{ 
        
    },
    props:{ 
        
    },
    data() {
        return {
            isShow:false,
        }
    },
    computed:{ },
    watch:{ },
    created() {
        
    },
    mounted() {
        
    },
    methods: {

    }
}
</script>
<style scoped>
.test{
    background-color: orange;
}
/* 进入的起点 
.test-enter{
        transform: translateX(-100%);

}
/* 进入的终点 
.test-enter-to{
        transform: translateX(0px);

}
离开的起点 
.test-leave{
        transform: translateX(0);

}
 离开的终点 
.test-leave-to{
        transform: translateX(-100%);

}*/
/*进入的起点  离开的终点 */ 
.test-enter,.test-leave-to{
        transform: translateX(-100%);

}
/*过程中*/
.test-enter-active,.test-leave-active{
    transition: 0.5s linear;
}
/* 进入的终点 离开的起点 */
.test-enter-to,.test-leave{
        transform: translateX(0px);

}

</style>

  transition 引入css     

 4.Vue中的AJAX

4.1 解决跨域问题

4.1.1  方法1

在vue.config.js中添加配置

devServer: {
    proxy: 'http://localhost:8090'
  } 

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

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

工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么请求会自动转发,但是若前端有那么就不会转发。

4.1.1  方法2 

在vue.config.js中添加配置

devServer: {
    proxy: {
      //请求前缀为erp的走代理
      '/test/erp': {
        //转发地址
        target: 'http://localhost:8090',
        pathRewrite:{'^/test':''},//重写地址
        ws: true,//用于支持webstocket,不写默认true
        changeOrigin: true//请求头中的host是否为虚拟地址,不写默认true
      },
      '/demo/erp': {
        //转发地址
        target: 'http://localhost:8090',
        pathRewrite:{'^/demo':''},//重写地址
        ws: true,//用于支持webstocket,不写默认true
        changeOrigin: true//请求头中的host是否为虚拟地址,不写默认true
      },
    }
  }

 changeOrigin: 

        true:服务器收到的host为虚拟的,与服务器地址相同。

        false:服务器收到的host为真实的,与请求地址相同。

        默认为true(react默认为false)。

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

缺点:配置略微繁琐,请求资源必须加前缀。(可以配置路径重写)。

axio,vue-resource

4.2slot插槽

(1)作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信额方式,适用于父组件===》子组件。

(2)分类:默认插槽,具名插槽,作用域插槽。

(3)使用方式:


        (3.1)默认插槽:

                    父组件:                    

<Category>
    <div>HTML结构</div>
</Category>

                    子组件:

<template>
    <div>
        <!--定义插槽-->
        <slot>插槽默认内容....</slot>
    </div>
</template>
        (3.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>
        (3.3)作用域插槽

                (3.3.1)作用:数组在组件自身,但是数据生成的结构由组件的使用者决定。

                                       (父组件决定结构,子组件提供数据)

                    父组件:

<Category>
    <template slot="scopeData">
        <!--生成ul列表-->
        <ul>
            <li v-for="g in scopeData.games" :key="g">{{g}}</li>
        </ul>
    </template>
    <template slot="scopeData">
       <!--生成h4标题-->
           <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4>
   </template>
</Category>

                    子组件:

<template>
    <div>
        <!--定义插槽-->
        <slot :games="games">插槽默认内容....</slot>
    </div>
</template>

5.vuex使用

5.1 理解vuex

5.1.1 vuex定义

(1)概念:专门在Vue中实现集中式状态(也叫:数据、状态数据,之后统称:状态)管理的VUe插件,对vue应用中多个组件共享状态进行集中式的管理(读/写),也是一种组件通信的方式,适用于任何组件。

(2)地址:GitHub - vuejs/vuex: 🗃️ Centralized State Management for Vue.js.

当某个属性,所有组件都需要使用:

5.1.2 使用场景:

(1)当多个组件依赖同一个状态。

(2)来自不同组件的行为要变更同一个状态。

5.2 搭建vuex环境

(1)创建 文件夹:src/store/index.js

//该文件用于vuex中最为核心的store

//引入Vue
import Vue from 'vue'


//引入vuex
import Vuex from 'vuex'

//准备actions 用于响应组件里的动作
const actions = {

}
//准备mutations 用于真正操作state中的数据
const mutations = {

}
//准备state 用于存储数据
const state = {

}
Vue.use(Vuex)

//创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state,
})

(2)在main.js中创建vm时传入store:

import store from './store'
const vm=new Vue({
    el:"#app",
    render:h=>h(App),
    store,
    beforeCreate(){
        Vue.prototype.$bus=this
    }
})

(3)注意:必须在index.js中引入Vue并使用插件vuex,否则new Vuex.Store()会报错。

5.3 基本使用

(1)初始化数据(state),配置actions,配置mutations,操作文件store.js。

//该文件用于vuex中最为核心的store

//引入Vue
import Vue from 'vue'


//引入vuex
import Vuex from 'vuex'

//准备actions 用于响应组件里的动作
const actions = {
    /* add(context,value){
        // console.log("store.actions.add,context=",context,"value=",value)
        contextmit("ADD",value)
    },
    sub(context,value){
        contextmit("SUB",value)
    }, */isOdd(context, value) {
        console.log("store.actions.isOdd",value)
        context.dispatch("demo1", value)
    }, demo1(context, value) {
        console.log("store.actions.demo1",value)
        context.dispatch("demo2", value)
    }, demo2(context, value) {
        console.log("store.actions.demo2")
        if (context.state.sum % 2 != 0) {
            console.log("store.actions.isOdd")
            contextmit("ADD", value)
        }
    }, sleepAdd(context, value) {
        console.log("store.actions.sleepAdd")
        setTimeout(() => {
            contextmit("ADD", value)
        }, 1000);
    }
}
//准备mutations 用于真正操作state中的数据
const mutations = {
    ADD(state, value) {
        // console.log("store.mutations.ADD,state=",state,"value=",value)
        console.log("store.mutations.ADD")
        state.sum += value
    },
    SUB(state, value) {
        console.log("store.mutations.state")
        // console.log("store.mutations.ADD,state=",state,"value=",value)
        state.sum -= value
    }
}
//准备state 用于存储数据
const state = {
    sum: 0,

}
Vue.use(Vuex)

//创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state,
})

(2)组件读取vuex中的数据:$taore.state.sum

(3)修改vuex中的数据:$store.dispatch('action中的方法名',参数)或者$storemit('mutations中的方法名',参数)。

<template>
  <div>
    <h1>当前求和为:{{ $store.state.sum }}</h1>
    <div class="sum">
      <select v-model.number="n">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
      </select>
      <button @click="add">+</button>
      <button @click="sub">-</button>
      <button @click="isOdd">为奇数执行+</button>
      <button @click="sleepAdd">sleep后+</button>
    </div>
  </div>
</template>

<script>
export default {
  props: {},
  data() {
    return {
      n: 1,
    };
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {},
  methods: {
    add() {
      // this.$store.dispatch("add", this.n);
      this.$storemit("ADD",this.n)
    },
    sub() {
      // this.$store.dispatch("sub", this.n);
      this.$storemit("SUB",this.n)
    },
    isOdd() {
      /* if (this.sum % 2 != 0) {
        this.add();
      } */
      this.$store.dispatch("isOdd", this.n);
    },
    sleepAdd() {
      this.$store.dispatch("sleepAdd", this.n);
    },
  },
};
</script>
<style scoped  lang="less">
.sum {
  select,
  button {
    font-size: 20px;
    min-width: 50px;
  }
}
</style>

备注:若没有网络请求或者其他业务逻辑,组件们可以越过actions直接访问mutations中的方法,既直接写commit()。

5.4 getters的使用

(1)概念:当state中的数据需要加工后在使用时,可以使用getters参数。

(2)配置getters属性:

//将state中的数据进行加工
const getters={
    bigSum(state){
        return state.sum*10
    }
}
//创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state,
    getters
})

(3)组件中读取数据:$store.getters.bigSum。

5.5 四个map方法

5.5.1 mapState方法:

用于帮助我们映射state中的数据为计算属性:

computed:{
 //借助mapState生成计算属性,从state中读取数据(对象写法)
    // ...mapState({school:'school',subject:'subject',sum:'sum'})
    // 数组写法
     ...mapState(['school','subject','sum']),
}

5.5.2 mapGetters方法:

用于帮助我们映射getters中的数据为计算属性

computed:{
 //借助mapState生成计算属性,从state中读取数据(对象写法)
    // ...mapGetters({bigSum:'bigSum'})
    // 数组写法
     ...mapGetters(['bigSum']),
}

5.2.3 mapActions方法

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

//对象式
// ...mapActions({sleepAdd:'sleepAdd',isOdd:'isOdd'})
//数组式
    ...mapActions(['sleepAdd','isOdd'])

5.2.4 mapMutations方法

用于帮助我们生成与mutations对话的方法,既:包含$storemit(xxx)的函数。

   //对象式
 // ...mapMutations({add:'ADD',sub:'SUB'}),
//数组式
    ...mapMutations(['ADD','SUB']),

5.2.5 注意

mapActions与mapMutations使用时,若需要参数,应在模板绑定事件时传递参数,否则参数是事件对象。

5.6 模块化+命名空间

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

(2)修改store.js,拆分为两个:person.js,count.js

person.js


//人员功能相关的
export default {
    namespaced:true,

    actions: {
        addPerson(context, value) {
            console.log("store.actions.addPerson")
            if (value != "") {
                contextmit("ADDPERSON", value)
            } else {
                alert("请输入姓名")
            }
        }
    },
    mutations: {
        ADDPERSON(state, value) {
            state.personList.push(value)
        }
    },
    state: {
        personList: ['张三']
    },
    getters: {}
}

conut.js


//求和功能相关的
export default {
    namespaced:true,
    actions: {
        isOdd(context, value) {
            console.log("store.actions.isOdd", value)
            if (context.state.sum % 2 != 0) {
                console.log("store.actions.isOdd")
                contextmit("ADD", value)
            }
        }, sleepAdd(context, value) {
            console.log("store.actions.sleepAdd")
            setTimeout(() => {
                contextmit("ADD", value)
            }, 1000);
        },
    },
    mutations: {
        ADD(state, value) {
            // console.log("store.mutations.ADD,state=",state,"value=",value)
            console.log("store.mutations.ADD")
            state.sum += value
        },
        SUB(state, value) {
            console.log("store.mutations.state")
            // console.log("store.mutations.ADD,state=",state,"value=",value)
            state.sum -= value
        },
    },
    state: {
        sum: 0,
    },
    getters: {
        bigSum(state) {
            return state.sum * 10
        }
    }
}

index.js

//该文件用于vuex中最为核心的store

//引入Vue
import Vue from 'vue'
//引入vuex
import Vuex from 'vuex'
import sumOptions from './count'
import personOptions from './person'
Vue.use(Vuex)

//创建并暴露store
export default new Vuex.Store({
    modules:{
        coundAbout:sumOptions,
        personAbout:personOptions,
    }
})

(3)namespaced:true开启命名空间。

(4)开启命名空间后组件,读取state中的数据:

//方式1 直接读取
this.$store.state.personAbout.personList
//方式2 借助mapState读取
...mapState('countAbout',[sum])

(4)开启命名空间后,组件读取getters中的数据:

//方式1 直接读取
this.$store.getters['personAbout/firstName']
//方式2 借助mapGetters读取
...mapState('countAbout',['bigSum'])

(5)开启命名空间后,组件调用dispatch:

//方式1 直接调用dispatch
this.$store.dispatch('personAbout/addPerson',{name:'王'})
//方式2 借助mapActions读取
...mapActions('countAbout',['isOdd','sleepAdd'])

(6)开启命名空间后,组件调用commit:

//方式1 直接调用commit
this.$storemit('personAbout/ADD_PERSON',{name:'王'})
//方式2 借助mapMutations读取
...mapMutations('countAbout',['ADD','SUB'])

6.vue中的路由

6.1 相关概念

6.1.1 vue-router 相关概念

vue中的一个插件库,专门用来实现SPA应用。

6.1.2 SPA应用相关概念

(1)单页Web应用(single page web application 既:SPA)。

(2)整个应用只有一个完整的页面。

(3)点击页面中的导航链接,不会刷新页面,只会做局部更新。

(4)通过ajax获取数据。

6.1.3 路由的相关概念

(1)路由的概念:

        (1.1)一个路由就是一组映射关系(key-value)。

        (1.2)key为路径,value是function或者component。

(2)路由分类:

        (2.1)后端路由:

                (2.1.1)概念:value是function,用于处理客户端提交的请求。

                (2.1.2)工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来

                               处理请求,返回响应数据。

        (2.2)前端路由:

                (2.2.1)概念:value是一个component,用于展示页面内容。

                (2.1.2)工作过程:当浏览器路径发生改变时,就会展示对应的组件。

6.2 路由的基本使用

(1)安装vue-router:命令:npm i vue-router(如果是vue2则使用npm i vue-router@3)。

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

(3)编写router配置项:创建router/index.js。

//该文件用于创建整个应用的路由器
import VueRouter from 'vue-router'
import Class from "../compnents/Class.vue";
import Subject from "../compnents/Subject.vue";
//创建并暴露一个路由
export default new VueRouter({
    routes:[

        {
        path:'/class',
        component:Class
    },{
        path:'/subject',
        component:Subject
    }
]
})

(4)实现切换:active-class表示点击后触发的样式,to表示跳转的路由连接。

      <div class="nav">
        <h3>导航</h3>
        <ul>
          <!-- router-like实现路由的切换,to表示拼接路径,actice-class表示点击后添加的class -->
          <li>
            <router-link to="/class" active-class="clickRouter"
              >班级</router-link
            >
          </li>
          <li>
            <router-link to="/subject" active-class="clickRouter"
              >学科</router-link
            >
          </li>
        </ul>
      </div>

(5)指定点击导航后展示的内容位置:

    <div class="content">
        <!-- router-view实现组件的切换 -->
        <router-view></router-view>
        <!-- <Subject />
      <Class /> -->
    </div>

6.3 注意事项

(1)通过路由配置实现展示的组件(既通过<router-view/>实现展示的组件)称之为路由组件,通过编写标签实现展示的,称之为一般组件,一般组件通常存放在components文件夹,路由组件一般存放在pages文件夹。

(2)通过切换实现展示后,不展示的路由组件默认是被销毁的,当切换路由展示路由组件时,会再去重新挂载。

(3)每个组件都有自己的$route属性,里面存储当前组件的路由信息(每个组件$route属性不相同)。

(4)整个组件只有一个router,可以通过$router属性获取到(每个组件$router属性相同)。

6.4 嵌套(多级)路由

(1)配置路由规则时使用children配置项:

​
//该文件用于创建整个应用的路由器
import VueRouter from 'vue-router'
import Class from "../pages/Classes";
import Subject from "../pages/Subject";
import OneClass from '../pages/OneClass'
import TwoClass from '../pages/TwoClass'
//创建并暴露一个路由
export default new VueRouter({
    routes: [
        {
            path: '/class',
            component: Class,
            children: [
                {
                    path: 'oneClass',
                    component: OneClass,
                },
                {
                    path: 'twoClass',
                    component: TwoClass,
                },
            ]
        }, {
            path: '/subject',
            component: Subject
        }
    ]
})

​

(2)使用时要写完整路径:

 <ul>
    <!-- router-like实现路由的切换,to表示拼接路径,actice-class表示点击后添加的class -->
    <li>
       <router-link to="/class/oneClass" active-class="clickRouter">一班</router-link>
    </li>
    <li>
       <router-link to="/class/twoClass" active-class="clickRouter">二班</router-link>
    </li>
</ul>

(3)注意:二级路由配置时不要写"/",但是访问时路径要写全路径(加上父路径)。

6.5 query参数

(1)传递参数:

        (1.1)直接拼接:

<ul>
          <!-- router-like实现路由的切换,to表示拼接路径,actice-class表示点击后添加的class -->
          <li v-for="item in classes" :key="item.index">
            <router-link
              to="/classes/class?name=一班&content=一班内容"
              active-class="clickRouter"
              >{{item.name}}</router-link>
 </ul>

        (1.2)对象写法:

        

<ul>
          <!-- router-like实现路由的切换,to表示拼接路径,actice-class表示点击后添加的class -->
          <li v-for="item in classes" :key="item.index">
            <router-link
              :to="{
                path: '/classes/class',
                query: {
                  name: item.name,
                  content: item.content,
                },
              }"
              active-class="clickRouter"
              >{{item.name}}</router-link
            >
          </li>
 </ul>

(2)接收参数:

<div>
     <h1>名称:{{$route.query.name}}</h1>
     <h1>内容:{{$route.query.content}}</h1>
</div>

6.6 命名路由

(1)作用:简化路由的跳转。

(2)使用:

        (2.1)给路由命名:

//该文件用于创建整个应用的路由器
import VueRouter from 'vue-router'
import Classes from "../pages/Classes";
import Subject from "../pages/Subject";
import Class from '../pages/Class'
//创建并暴露一个路由
export default new VueRouter({
    routes: [
        {   
            name:'班级',
            path: '/classes',
            component: Classes,
            children: [
                {   
                    name:'班内信息',
                    path: 'class',
                    component: Class,
                },
                {
                    path: 'subject',
                    component: Subject
                }
            ]
        }
    ]
})

        (2.2)使用:

       

 <router-link
              :to="{
                path: '/classes/class',
                query: {
                  name: item.name,
                  content: item.content,
                },
              }"
              active-class="clickRouter"
              >{{item.name}}
</router-link>

<!--简化为-->

 <router-link
              :to="{
                name: '班内信息',
                query: {
                  name: item.name,
                  content: item.content,
                },
              }"
              active-class="clickRouter"
              >{{item.name}}
</router-link>

若是路径过于繁琐,可以不使用路径,转而使用name属性。

6.7 params参数

(1)配置路由,声明接收params的参数(path中使用占位符):

//该文件用于创建整个应用的路由器
import VueRouter from 'vue-router'
import Classes from "../pages/Classes";
import Subject from "../pages/Subject";
import Class from '../pages/Class'
//创建并暴露一个路由
export default new VueRouter({
    routes: [
        {   
            name:'班级',
            path: '/classes',
            component: Classes,
            children: [
                {   
                    name:'班内信息',
                    path: 'class/:name/:content',
                    component: Class,
                },
                {
                    path: 'subject',
                    component: Subject
                }
            ]
        }
    ]
})

(2)传递参数:

 <li v-for="item in classes" :key="item.index">
      <router-link to="/classes/class/班级名称/内容" active-class="clickRouter">            
        {{item.name}}</router-link>
</li>
<!--或者-->
<li v-for="item in classes" :key="item.index">
            <router-link
              :to="{
                name: '班内信息',
                params: {
                  name: item.name,
                  content: item.content,
                },
              }"
              active-class="clickRouter">{{ item.name }}</router-link >
</li>

注意:如果用对象写法一定要用name属性跳转,而不是path属性。

(3)接收参数:

<div>  
        <h1>名称:{{$route.params.name}}</h1>
        <h1>内容:{{$route.params.content}}</h1>
</div>

6.8 props配置

(1)作用:让路由组件更加方便的接受到参数:

{
   name: '班内信息',
   path: 'class/:name/:content',
   component: Class,
   // 第一种写法 值为对象 局限性比较大一般不用
   /*props:{
   name:"name",
   content:"content"
   } */
   // 第二种写法 值为Boolean 如果值为true,则将所有收到的param参数以props的形式传递给该组件,query接收不到
   // props:true
   // 第三种写法 值为函数 
   props($route){
      return{
               name:$route.query.name,
               content:$route.query.content
            }
         }
}

6.9 route-link的replace

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

(2)浏览器历史记录写入模式有两种,push和replace,push是追加历史记录,replace是替换当前记录,默认是push。

(3) <route-link replace>或<route-link :replace='true'>开启replace模式。

6.10 编程式路由导航

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

(2)编码:

pushShow(item){
      this.$router.push({
                name: '班内信息',
                query: {
                  name: item.name,
                  content: item.content,
                },
              })
    },
replaceShow(item){
      this.$router.replace({
                name: '班内信息',
                query: {
                  name: item.name,
                  content: item.content,
                },
              })
    },
//返回
back() {
      this.$router.back();
    },
//前进
forward() {
      this.$router.forward();
    },
//前进或后退指定步数
testGo() {
      this.$router.go(-1);
    },

6.11 路由缓存

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

(2)具体编码:

<keep-alive include="Classes">
          <router-view ></router-view>
 </keep-alive>

(3)注意:include如果不写则默认全部,内容为组件的name属性,并非router内配置的name属性。缓存多个写法为:

<keep-alive :include="['Classes','Subject']">
          <router-view ></router-view>
 </keep-alive>

6.12 新生命周期钩子

(1)作用:路由组件所独有的生命周期钩子,用于捕获组件是否处于激活状态

(2)使用:

activated() {
    console.log("Class被激活")
    this.timeId=  setInterval(() => {
    this.opacity -= 0.01;
      if (this.opacity < 0) {
        this.opacity = 1;
      }
    },15);
  },
  deactivated() {
        console.log("Class失活")
    clearInterval(this.timeId)
  },

(3)补充:$nextTick也属于生命舟曲钩子,当更新数据的dom更新完毕再执行$nextTick内的代码。

6.13 路由守卫

(1)作用:对路由进行权限控制。

(2)分类:全局守卫,独享守卫,组件内守卫。

(3)全局守卫:

//全局前置路由守卫,每次初始化之前或者路由切换之前,
router.beforeEach((to, from, next) => {
    console.log("前置路由守卫",from,to)
    let user = localStorage.getItem("user")
    if (to.meta.isAuth) {
        if (user == 'user') {
            next()
        }else{
            alert(user+'无权查看')
        }
    }else{
        next()
    }
})
//全局前置路由守卫,每次初始化之前或者路由切换之后,
router.afterEach((to, from) => {
    console.log("后置路由守卫",from,to)
    document.title=to.name
})

(4)独享守卫:

独属于某一个路由的路由守卫,且没有afterEnter路由守卫。

{
                    name: '班内信息',
                    path: 'class/:name/:content',
                    meta:{
                        isAuth:true
                    },
                    component: Class,
                    beforeEnter:(to, from, next) => {
                        console.log("前置路由守卫",from,to)
                        let user = localStorage.getItem("user")
                        if (to.meta.isAuth) {
                            if (user == 'user') {
                                next()
                            }else{
                                alert(user+'无权查看')
                            }
                        }else{
                            next()
                        }
                    }
                }

(5)组件内路由守卫:

通过路由规则,访问或离开该组件时,会调用。(直接使用标签访问组件不会调用)

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

6.14 路由的hash和history

(1)对于一个url来说,#及其后面的内容就是hash值。

(2)hash值不会包含在http请求中。(既不会发送给服务器)

(3)hash模式:

        (3.1)地址中永远带有#,不美观。

        (3.2)若以后通过第三方手机app分享,若app校验较为严格,则会被认定为不合法。

        (3.3)兼容性好。

(4)history模式:

        (4.1)地址干净,美观。

        (4.2)兼容性比起hash模式略差。

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

 8.Vue3快速上手

vue3地址 

地址:awesome vue 3 & vue-cli 3+ · GitHub

 8.2 Vue3优势

(1)性能的提升。

(2)源码升级。

(3)支持TypeScript。

(4)新特性。

8.1创建Vue3工程

 官方文档:快速上手 | Vue.jsVue.js - 渐进式的 JavaScript 框架https://cn.vuejs/guide/quick-start.html

8.1.1 vue-cli创建

##1.查看vue-cli版本:
vue --version 
vue -V

##2.如果版本在4.5.0以下升级或安装执行:
npm install -g @vue/cli 

##3.创建:
vue create vue3_test

##4.启动:
cd vue_test
npm run serve

 8.1.2 vite 创建

vite 官网:

Vite | 下一代的前端工具链下一代前端工具链https://cn.vitejs.dev/weback打包模式:

vite打包模式:

##1.创建工程:
npm init vite-app <project-name>

##2.进入工程目录:
cd <project-name>

##3.安装依赖:
npm install

##4.启动:
npm run serve

 8.2常用composition API

组合式API

8.2.1 setup函数

(1)概念:Vue3中的一个新的配置项,值为一个函数。

(2)setup是所有Compositon API的“表演的舞台”。

(3)组件中所用到的:数据,方法等等,均要在setup中配置。

(4)setup函数中的两种返回值:

        (4.1)若返回一个对象,则对象中的属性,方法,在模板中均可以直接使用(常用)。

        (4.2)若返回一个渲染函数,则可以自定义渲染内容(了解)。

(5)注意:

        (5.1)尽量不要与Vue2.x混用:

                (5.1.1)Vue2.x中的(data,methods,computed等等)可以访问到setup的方

                               法,属性。

                (5.1.2)setup中不能访问到Vue2.x中的(data,methods,computed等等)。

                (5.1.3)如果重名,setup优先级高。

        (5.2)setup不能是一个async函数,因为返回值不再是一个return对象,而是

                    promise,模板看不到return中的属性

总结:setup用于替代 Vue2 中的 beforeCreate 和 created 钩子函数。setup 选项是一个函数,它在组件实例被创建之前执行,并返回一个包含状态和方法等配置信息的对象。

import { h } from "vue";
export default {
  name: "App",
  setup() {
    let person = {
      name: "张三",
      age: 18,
    };
    function sayHello() {
      alert(`${person.name}:hello word,年龄:${person.age}`);
    }
    //返回一个对象
    return {
      person,
      sayHello,
    };
    //返回一个渲染对象
    /* return ()=>{
    return h('h1',person.name)
  } */
  },
};

8.2.2 ref函数

(1)作用:用于定义一个响应式的数据。

(2)语法:const xxx=ref(initValue)。

        (2.1)创建一个包含响应式数据的引用对象(reference对象)。

        (2.2)js中操作数据用xxx.value操作。

        (2.3)模板中读取:直接{{xxx}}。

(3)备注:

        (3.1)接收基本类型:响应式还是依靠Object.defineProperty()的get与set实现。

        (3.2)接收对象类型:内部使用了Vue3中的一个新函数reactive函数,底层是用Proxy

                     实现的。

import { ref } from "vue";
export default {
  name: "App",
  setup() {
    /* let person = {
      name: ref("张三"),
      age: ref(18),
    }; */
     let person =ref({
      name: "张三",
      age: 18
    });
    function changeInfo(){
      console.log(person)
      person.value.age=25
    }
    return {
      person,
      changeInfo
    };
  },
};

8.2.3 reactive函数

(1)作用:定义一个响应式数据(基本数据不用他,用ref函数)。

(2)语法:const 代理对象=reactive(源对象) 接受一个对象或数组,返回一个代理对象(proxy对象)。

(3)reactive定义的响应式数据是深层次的。

(4)内部基于es的proxy实现,通过代理对象操作源对象内部的数据。

let person = reactive({
      name: "张三",
      age: 18,
      hobby: [1, 2, 3, 4, 5],
      a: {
        b: {
          c: {
            d: 666,
          },
        },
      },
    });

    function changeInfo() {
      person.hobby[0]=111;
    }
    return {
      person,
      changeInfo,
    };

8.2.4 响应式原理

8.2.4.1 vue2中的响应式

对象类型:通过Object.defineProperty()对属性的读取,修改进行拦截(数据劫持)。

数组类型:通过重写数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。

问题:

        (1)新增属性,删除属性,界面不会更新。(监测不到数据修改)

        (2)不通过制定方法修改数组,界面不会自动更新。(监测不到数据修改)

​
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

</head>

<script>
    //源数据
    let person = {
      name: "张三",
      age: 18,
    };
    //模拟vue2中的响应式实现原理
    let p = {};
    Object.defineProperty(p, "name", {
      get() {
        console.log("有人读取了p.name属性");
        return person.name;
      },
      set(value) {
        console.log("有人修改了p.name属性为:", value);
        person.name = value;
      },
    });
    Object.defineProperty(p, "age", {
      get() {
        console.log("有人读取了p.age属性");
        return person.age;
      },
      set(value) {
        console.log("有人修改了p.age属性为:", value);
        person.age = value;
      },
    });
    </script>
    
<body>
</body>

</html>

​

当我们创建一个响应式对象,修改其中的属性会触发相应的方法,而我们添加的属性,并不是响应式的,并非通过get和set方法操作属性,而vue2的更新dom操作就是在set方法中实现的,所以监控不到属性的新增和删除。

具体参考:1.Vue核心-CSDN博客

Vue核心的1.5小结数据代理以及1.10.5 Vue无法检测到的数据改变和解决方案。

8.2.4.2 vue3中的响应式

(1)了解Reflect(反射):

 let person = {
      name: "张三",
      age: 18,
    };
    let p={}

当出现如下代码时,会出现语法错误导致页面无法正常渲染。

Object.defineProperty(p, "name", {
      get() {
        console.log("有人读取了p.name属性");
        return person.name;
      }
    });
    Object.defineProperty(p, "name", {
      get() {
        console.log("有人读取了p.name属性");
        return person.name;
      }
    });

当我们使用Reflect操作时,失败并不会出现错误,而是返回一个false。

  const a= Reflect.defineProperty(p, "name", {
      get() {
        console.log("有人读取了p.name属性");
        return person.name;
      }
    });
    const b= Reflect.defineProperty(p, "name", {
      get() {
        console.log("有人读取了p.name属性");
        return person.name;
      }
    });
    console.log('a',a,'b',b)
a true b false

所以,当我们使用第一种方法,去操作一个属性有可能会出现异常,导致页面出现问题,而是用第二种方式去操作,会返回一个操作结果是否成功。站在代码兼容性考虑,是有必要使用Reflect来操作属性的。

(2)了解Proxy:

p=new Proxy(person,{
        //读取属性调用
        get(target,propName){
            console.log('有人读取了p的',propName,"属性,值",person.propName)
            return target[propName]
        },
        //修改,新增属性调用
        set(target,propName,value){
            console.log('有人修改了p的',propName,"属性,原值",person.propName,'改为:',value)
             target[propName]=value
        },
        //删除属性调用
        defineProperty(target,propName){
            console.log('有人删除了了p的',propName,"属性")
           return delete target[propName]
        },
        
    }) 

创建一个Proxy对象当我们对该对象进行属性操作时:

当我们对对象的属性进行增删改查操作时,都会被相应的监控的,并且执行的操作会在person上执行。

(3)总结:当结合了Proxy和Reflect后就形成了vue3的响应式:

                   Proxy(代理):拦截对象中任意属性的变化(增删改查)。

                   Reflect(反射):对被代理对象的属性执行操作(不会出现语法错误)。

p=new Proxy(person,{
        //读取属性调用
        get(target,propName){
            console.log('有人读取了p的',propName,"属性,值",person.propName)
            return Reflect.get(target,propName)
        },
        //修改,新增属性调用
        set(target,propName,value){
            console.log('有人修改了p的',propName,"属性,原值",person.propName,'改为:',value)
             Reflect.set(target,propName,value)
        },
        //删除属性调用
        defineProperty(target,propName){
            console.log('有人删除了了p的',propName,"属性")
           return Reflect.defineProperty(target,propName)
        },
        
    }) 

Proxy和Reflect的浅层理解:Proxy和Reflect-CSDN博客

8.2.5 reactive和ref

从定义角度相比:

        ref:用来定义基本数据类型。

        reactive:用来定义对象或数组类型数据。

从原理角度相比:

        ref:通过Object.defineProperty()的get和set来实现响应式(数据劫持)。

        reactive:通过Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内数据。

从使用角度相比:

        ref:操作数据需要用xxx.value,读取时不需要xxx.value。

        reactive:操作和读取数据都不用xxx.value

8.2.6 setup的两个注意点

(1)setup执行时机在beforeCreate之前,且只执行一次,this是undefined。

(2)setup的参数:

        (2.1)props:值对象,包含:组件外传递过来的,且组件内部生命接受了的属性。

        (2.2)context:上下文对象:

                (2.2.1)attrs:值对象,外部传递进来,但props未接收的属性(this.$attrs)。

                (2.2.2)slots:收到的插槽内容,相当于this.$slots。(最好用v-slot命名)。

                (2.2.3)emit:分发自定义事件的函数(this.$emit)。

8.2.7 计算与监视属性

8.2.7.1 计算属性 computed

(1)与vue2.x的computed配置功能一致。

(2)写法:   

import { computed } from "vue";
setup(props, context) {
    const data = reactive({
      person: {
        name: {
          firstName: "张",
          lastName: "三",
        },
        age: 18,
      },
    });
    //计算属性--简写--只考虑回写没考虑修改
    /* data.person.fullName=computed(()=>{
       return data.person.name.firstName+"--"+ data.person.name.lastName
    }) */
    //计算属性--完整写法
    data.person.fullName = computed({
      get() {
        return data.person.name.firstName + "-" + data.person.name.lastName;
      },
      set(value) {
        const nameArr = value.split("-");
        data.person.name.firstName = nameArr[0];
        data.person.name.lastName = nameArr[1];
      },
    });
    return {
      data,
    };
  },
8.2.7.2 监视属性 watch

与vue2中的watch功能一致。

问题:

        (1)监视reactive定义的响应式数据时,oldValue无法正确获取(由于旧值和新值实际

                 上是同一个引用,因此旧值和新值看起来总是相同的。),且默认开启了深度监

                 视,无法关闭(deep配置失效)。

        (2)监视reactive定义的响应式数据中的某个对象属性时,deep配置又生效了。

 //情况1:监视ref定义的一个响应式数据
    watch(sum,(newValue, oldValue)=>{
        console.log("sum被修改了", newValue, oldValue);
    }) 

    //情况2:监视ref定义的多个响应式数据
    watch([sum,msg],(newValue, oldValue)=>{
        console.log("sum或msg被修改了", newValue, oldValue);
    })
    //传入第三个参数配置对象
    watch(sum,(newValue, oldValue)=>{
        console.log("sum被修改了", newValue, oldValue);
    },{immediate:true})
    /* 
        情况3:监视reactive定义的对象 全部属性
        1.reactive定义的数据无法正确获取oldValue 
        2.强制开启了深度监视且无法关闭(deep配置失效了)
    */
    watch(data,(newValue,oldValue)=>{
        console.log('person变化了',newValue,oldValue)
    })
    /* 
        情况4:监视reactive定义的对象 某个属性
    */
    watch(()=>data.age,(newValue,oldValue)=>{
        console.log('person变化了',newValue,oldValue)
    })
    /* 
        情况5:监视reactive定义的对象 某些属性
    */
     watch([()=>data.age,()=>data.name],(newValue,oldValue)=>{
        console.log('person变化了',newValue,oldValue)
    }) 
    /* 
        特殊情况:监视reactive定义的对象 中的对象属性
    */
     watch(()=>data.job,(newValue,oldValue)=>{
        console.log('person变化了',newValue,oldValue)
    },{deep:true}) 
8.2.7.2 补充

(1)监视ref定义的基本数据类型的值时,不可以用xxx.value否则无法监视到。

(2)监视ref定义的对象属性时,需要用xxx.value否则由于内存中地址未改变,无法监视到ref请求reactive生成的Proxy的代理类,也可以开深度监视来实现,监视对象属性中的内容。

8.2.7.3 wactcEffect函数

watcg:既要指明监视的属性,也要指明监视的回调。

watchEffect:不指明监视的属性,在回调中用到了哪个属性就监视哪个属性。

watchEffect优点类似于computed:

        computed注重的是结果(回调函数的返回值),所以必须return。

        watchEffect注重的是过程(回调的函数体),所以不用写返回值。

    watch(data, (newValue, oldValue) => {
      console.log("person变化了", newValue, oldValue);
    },{immediate:true}); 

    watchEffect(()=>{
      console.log('watchEffect的回调')
      const x=data.age
    })

8.2.8 Vue3中的生命周期

Vue3.0中可以继续使用Vue2.x的生命周期钩子,但是有两个被更名:        

beforeDestroy===》beforeUnmount
destroyed    ===》unmounted

Vue3.0也提供了Composition API形式的生命周期钩子,与Vue2.x对应关系如下:

       

beforeCreate   ===>    setup()
created        ===>    setup()
beforemount    ===>    onBeforeMount
mounted        ===>    onMounted
befoerUpdate   ===>    onBeforeUpdate
updated        ===>    onUpdated
beforeUnmount  ===>    onBeforeUnmount
unmounted      ===>    onUnmounted

App:

<template>
  <Demo >
  </Demo>
</template>

<script>
import Demo from "./componets/Demo.vue";
export default {
  name: "App",
  components: {
    Demo,
  },
  setup() {
    
   
  },
};
</script>

Demo:

<template>
  <div class="container">
    <!-- <h1>当前求和值为:{{ sum }}</h1>
    <button @click="add">点我++</button>
    <h1>当前求和值为:{{ msg }}</h1>
    <button @click="editMsg">点我修改信息</button> -->
    <h1>姓名:{{ data.name }}</h1>
    <h1>年龄:{{ data.age }}</h1>
    <button @click="data.name+='@'">修改姓名</button>
    <button @click="data.age++">点我修改年龄</button>
  </div>
</template>
<script>
import { reactive, toRefs, onBeforeMount, onMounted,watch,ref } from "vue";
export default {
  name: "App",
  watch: {
    //简写无法深度监视
    /* data(newValue,oldValue){
        console.log('sum被修改了',newValue,oldValue)
    } */
    //完整写法
   /*  data: {
      immediate: true,
      deep:true, 
      handler(newValue, oldValue) {
        console.log("sum被修改了", newValue, oldValue);
      },
    }, */
  },
  setup() {
    let sum=ref(0)
    let msg=ref('信息')
    const data = reactive({
      name: '张三',
      age:18,
      job:{
        j1:1000
      }
    });
    
    //情况1:监视ref定义的一个响应式数据
    watch(sum,(newValue, oldValue)=>{
        console.log("sum被修改了", newValue, oldValue);
    }) 

    //情况2:监视ref定义的多个响应式数据
    watch([sum,msg],(newValue, oldValue)=>{
        console.log("sum或msg被修改了", newValue, oldValue);
    })
    //传入第三个参数配置对象
    watch(sum,(newValue, oldValue)=>{
        console.log("sum被修改了", newValue, oldValue);
    },{immediate:true})
    /* 
        情况3:监视reactive定义的对象 全部属性
        1.reactive定义的数据无法正确获取oldValue 
        2.强制开启了深度监视且无法关闭(deep配置失效了)
    */
    watch(data,(newValue,oldValue)=>{
        console.log('person变化了',newValue,oldValue)
    })
    /* 
        情况4:监视reactive定义的对象 某个属性
    */
    watch(()=>data.age,(newValue,oldValue)=>{
        console.log('person变化了',newValue,oldValue)
    })
    /* 
        情况5:监视reactive定义的对象 某些属性
    */
     watch([()=>data.age,()=>data.name],(newValue,oldValue)=>{
        console.log('person变化了',newValue,oldValue)
    }) 
    /* 
        特殊情况:监视reactive定义的对象 中的对象属性
    */
     watch(()=>data.job,(newValue,oldValue)=>{
        console.log('person变化了',newValue,oldValue)
    },{deep:true}) 
    
    return {
      data,
    //   add,sum,msg,editMsg
    };
  },
};
</script>
<style lang="scss" scoped>
</style>

8.2.9 自定义hook函数

定义:本质是一个函数,把steup函数中使用的Composition API进行的封装。

类似于Vue2.x中的mixin。

自定义hook的优势:服用代码,让setup中的逻辑更加清楚。

新建hooks文件夹,并创建功能对应的js文件(以点击获取鼠标当前xy轴位置为例):

 userPoint.js

import {
    reactive,
    onMounted,
    onBeforeUnmount
} from "vue";

export default  function () {
    let point = reactive({
        x: 0,
        y: 0
    })
    function savePiont(event) {
        point.x = event.pageX
        point.y = event.pageY
        console.log(event.pageX)
        console.log(event.pageY)
    }
    onMounted(() => {
        window.addEventListener('click', savePiont)
    }),
        onBeforeUnmount(() => {
            window.removeEventListener('click', savePiont)
        })
    return point
}

Demo.vue

<template>
  <div class="container">
    <h1>当前鼠标X:{{point.x}},Y:{{point.y}}</h1>
  </div>
</template>
<script>
import {
  reactive
} from "vue";
import userPoint from '../hook/userPoint'
export default {
  name: "Demo",
  watch: {},
  setup() {
      let point= userPoint()
   
    
    //使用组合式API
    return {
      point
    };
  }
};
</script>
<style lang="scss" scoped>
</style>

8.2.10 toRef

作用:创建一个ref对象,其value指向另一个对象中的某个属性。

语法:const name=toRef(person,'name')。

应用:将响应式对象中的某个属性单独提供给外部使用时。

扩展:toRefs与toRef功能一致,但是可以创建多个ref对象,已发toRefs(person)。

<template>
  <h1>Vue3</h1>
  <h2>
  姓名: {{ name }}<br>
  年龄:{{ person.age }}<br>
  性别:{{ person.sex }}<br>
  爱好:{{ person.hobby }}<br>
  爱好:{{ person.job.j1.pay }}<br>
  <button @click="changeName">修改名字信息</button>
  <button @click="deleteAge">删除属性年龄</button>
  <button @click="addSex">添加一个属性sex</button>
  <button @click="changeArr">通过下标修改数组</button>
  <button @click="person.job.j1.pay+=100">涨薪</button>
  </h2>
</template>

<script>
import { reactive,toRef,toRefs } from "vue";
export default {
  name: "App",
  setup() {
    let person = reactive({
      name: "张三",
      age: 18,
      hobby: [1, 2, 3, 4, 5],
      job:{
        j1:{
          pay:1000
        }
      }
    });

    function changeName() {
      person.name="李四"
    }
    function addSex() {
      person.sex="男"
    }
    function changeArr() {
      person.hobby[2]=999
    }
    function deleteAge() {
      delete person.age
    }
    return {
      person,
      /* name:toRef(person,'name'),
      pay:toRef(person.job.j1,'pay'), */
      ...toRefs(person),
      addSex,
      changeArr,
      changeName,
      deleteAge,
    };
  },
};
</script>

8.3 其他Composition API

8.3.1 shallowReactive和shallowRef

shallowReactive:只处理对象最外层属性的响应式(浅响应式)。

shallowRef:只处理基本数据类型的响应式,不进行对象响应式的处理。

使用场景:

        如果有一个对象,结构比较深,但是变化时值是外层属性变化===》shallowReactive。

        如果有一个对象,后续不会修改对象属性,而是产生新的对象替换===》shallowRef。

8.3.2 readonly和shallowReadonly

readonly:让一个响应式数据变为只读的(深只读)。

shallowReadonly:让一个响应式数据变为只读的(浅只读)。

使用场景:不希望数据被修改时。

8.3.3 toRaw和markRaw

toRaw:

        作用:讲一个有reactive生成的响应式对象转为普通对象。(ref的不行)

        使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作不会引起

                          页面的更新。

markRaw:

        作用:标记一个对象,使其永远不会在成为响应式对象。

        使用场景:

                (1)如果我们声明了一个reactive的对象,后续对这个对象进行属性添加的操作,

                         添加上的属性默认就是响应式的,而有些值不应该设置为响应式的,例如:

                         异常复杂的第三方类库等等。

                (2)当显然具有不可变数据源的大列表时,条过响应式转换可以提高性能。

8.3.4 customRef

作用:创建一个自定义的ref,并对其依赖项跟踪和更新出发进行显示控制(可以实现自定义的控制页面更新时机,track告诉get方法这个参数需要跟踪,triger通知vue更新dom)。

实现防抖:

<template>
  <h1>Vue3</h1>
  <h2>
    <input type="text" v-model="keyWords" />
    {{ keyWords }}
  </h2>
</template>

<script>
import { reactive, ref, customRef } from "vue";
export default {
  name: "App",
  setup() {
    //自定义ref
    function myRef(value,delay) {
      let timer;
      return customRef((track,triger) => {
        return {
          get() {
            console.log("---myRef里的get---",value);
            track()//通知vue监视这个值的改变
            return value;
          },
          set(newValue) {
            console.log("---myRef里的set---",newValue);
            //通知vue重新解析模板
            clearTimeout(timer)
           timer= setTimeout(() => {
              value=newValue
              triger()
            }, delay);
          },
        };
      });
    }
    // let keyWords=ref('hello')//使用Vue提供的ref
    let keyWords = myRef("hello",500); //使用自己的ref
    return {
      keyWords,
    };
  },
};
</script>

8.3.5 provide与inject

作用:实现祖与后代组件之间的通信。

父组件中有一个provide选项来提供数据,后代组件有一个inject选项来开始使用这些数据。

具体写法:

祖组件:

setup() {
    let car=reactive({
      name:'奔驰',
      price:400000
    })
    //给自己的后代组件传递数据
    provide('car',car)

后代组件:

 let car=inject('car')
    console.log("二级子组件",car)
  },

8.3.6 响应式数据的判断

isRef:检查一个值是否为ref对象。

isReactive:检查一个值是否是由reactive创建的响应式代理。

isReadonly:检查一个对象是否是由readonlu创建的只读代理。

isProxy:检查一个对象是否由reactive或者readonly方法创建。

8.4 Composition API的优势

8.4.1 Options API 

传统的OptionsAPI(配置API)中,新增或修改一个需求,要分别在data,methods,computed里修改

8.4.2 Composition API

让相同功能的变量,函数等等更加有序的组织在一起(借助hook函数)。

8.5 新的组件

8.5.1 Fragment

在Vue2.x中,组件必须有一个跟标签。

在Vue3中,组件可以没有跟标签,内部会将多个标签包含在一个Fragment的虚拟内存中。

优势:减少标签层级,减小内存占用。

8.5.2 Teleprot

概念:Teleprot是一种能够将我们组件中的html结构移动到指定位置的技术。

<template>
  <div>
    Dialog
    <button @click="isShow = true">点我弹框</button>
    <teleport to="body">
      <div class="dialog" v-show="isShow">
        弹框 内容
        <button @click="isShow = false">关闭</button>
      </div>
    </teleport>
  </div>
</template>
<script>
import { ref } from "vue";
export default {
  name: "Dialog",
  setup() {
    let isShow = ref(false);
    return {
      isShow,
    };
  },
};
</script>
<style  scoped>
.dialog {
  background-color: green;
  width: 300px;
  height: 300px;
}
</style>

将代码直接传送到html的body内。

8.5.3 Suspense

等待异步组件时渲染一些额外内容,让应用有更好的用户体验。

使用步骤:

        异步引入组件:

import { defineAsyncComponent } from "vue";
const Demo = defineAsyncComponent(() => import("./componets/Demo1.vue"));

使用Supense包裹组件,并配置好default与fallback:

<Suspense>
      <template v-slot:default>
        <Demo></Demo>
      </template>
      <template v-slot:fallback>
        <div>
          <h2>加载中....</h2>
        </div>
      </template>
    </Suspense>

8.6 其他

8.6.1 全局API转移

Vue2.x中有序地全局API和配置。

例如:注册全局组件,注册全局指令等等

//全局组件
Vueponent('MyButton',{
data:()=>({
    cont:0
}),
template:'<button @click='cont++'>{{cont}}++</button>'
})
//全局指令
Vue.directive('focus',{
    inserted:el=>el.focus()
})

Vue3对这些API做出了调整:

        将全局API(Vue.xxx)调整到应用实例app上

8.6.2 其他改变

8.6.2.1 data选项

data选项始终都要被声明为一个函数。

8.6.2.2 过渡类名的更改

        Vue2.x写法:

v-enter,
v-leave-to{
    opacity:0
}
v-leave,
v-enter-to{
    opacity:1
}

        Vue3写法:

.v-enter-from,
v-leave-to{
    opacity:0
}
.v-leave-from,
v-enter-to{
    opacity:1
}
8.6.2.3 移除keyCode

移除keyCode作为v-on修饰符,同时也不再支持config.keyCodes

8.6.2.4 移除native

移除native作为v-on修饰符:

        父组件绑定事件:

<my-component 
    v-on:close='dialogClose'
    v-on:click='dialogClick'
/>

        子组件js声明自定义事件(不声明则被认为是js原生事件):

export default {
    emits:['close']
}
8.6.2.5 移除过滤器

过滤器虽然看起来很方便,但是他需要一个自定义语法,打破大括号内表达式只是JavaScript的假设,不仅有学习成本还有实现成本,建议用方法或计算属性替换过滤器。

7.Vue UI库

7.1移动端常用的UI

(1) Vant:Vant 4 - A lightweight, customizable Vue UI library for mobile web apps.A lightweight, customizable Vue UI library for mobile web apps.https://vant-ui.github.io/vant/#/zh-CN

(2)Cube UI:cube-ui: cube-ui 是由滴滴开源的基于 Vue.js 实现的移动端组件库https://gitee/mirrors/cube-ui

(3)Mint UI:

Mint UIhttps://mint-ui.github.io/#!/zh-cn

7.2 PC端常用UI

(1)Element UI:

Element - The world's most popular Vue UI frameworkElement,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库https://element.eleme.io/(2)IView UI:

iView / View Design 一套企业级 UI 组件库和前端解决方案基于 Vue.js 的 UI 组件库,用于研发企业级中后台产品。iView 官网。https://www.iviewui/

使用UI组件库一般按需引入。

 欢迎补充。

本文标签: 学习笔记