浅谈Vue

一、初识Vue

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

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

  3. root容器里面的代码成为vue模板:先有容器后有vue实例

  4. 容器与vue实例是1对1关系,一个容器只能支持Vue实例

  5. 区分js表达式与js代码

    js表达式:一个表达式会生成一个值,可以放在人和需要一个值的地方

    {{js表达式}}->{{1+1}},{{函数方法调用}}

 <!-- 准备一个容器(模板) -->
    <div id="root">
        <h1>你好,{{name}},{{age}}</h1>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false;//直至vue在启动时生产提示
        new Vue({//容器与vue实例建立连接
            el: '#root',//el用于指定当前vue实例为哪个容器服务,之通常为css选择器字符串
            data:{//data中用于存储数据,数据共el锁只当的容器去使用,值我们先用对象
                name: 'chen',
                age: 18
            }
        });
    </script>

二、模板语法

  • v-bind:属性名 由此vue会把属性名当作一个js表达式,从data对象中取数据

  • v-bind可以简写为:

  • vue模板语法有2大类

    1. 插值语法:

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

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

    2. 指令写法:

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

      ​ eg: v-bind:href="xxx"或者简写:href="xxx",xxx同样是js表达式,可以取到data中所有属性

      ​ 备注: vue有很多指令,形式都是:v-???

  • 具有多层级

    <div id="root">
            <h1>插值语法,{{name}}</h1>
            <h2>指令语法<a  v-bind:href="url" v-bind:x="hello">博客链接</a></h2>
            <h2>v-bind简写 <a :href="url" :x="hello">这也是博客链接</a></h2>
            <h2>学校名称是{{school.name}}</h2>
        </div>
        <script>
            new Vue({
                el: '#root',
                data:{
                    name:'陈皮',
                    url: 'http://www.randycout.com',
                    hello: 'v-bind可以跟任何属性',
                    school: {
                        name: '清华'
    
                    }
                }
            })
        </script>
    

三、el和data的两种写法

  1. el的两种写法

    • new Vue的时候写el属性

      new Vue({
      	el: '#root'
      })
      
    • 先创建Vue实例,然后再通过app.$mount('#root')指定el的值

      vm.$mount('#root');//el的第二种写法,这种写法更加灵活
      
  2. data的两种写法

    • 对象式

      new Vue({
      	data: {
      		name: 'chen'
      	}
      })
      
    • 函数式:创建组件时必须用函数式

      new Vue({
      	data:function(){
      		return{
      			name: 'chen'
      		}
      	}
      })
      

四、数据绑定

Vue中有两种数据绑定方式:

1. `v-bind`(单项绑定):数据只能从vue实例的data中流向页面
 - 可以简写为成 `:属性`
2. `v-model`(双向绑定):数据可以在data和页面中双向流动
 - 双向绑定一般都应有在表单元素上,eg:input,select
 - `v-model:value`可简写成`v-mode`,默认收集的就是元素`value`值
 <!-- 容器,或者说是模板 -->
    <div class="root">
        <input type="text" v-bind:value="name">
        <input type="text" v-model:value="name">
        <!-- 简写效果 -->
        <input type="text" :value="sex">
        <input type="text" v-model="sex">
    </div>
    <script>
        Vue.config.devtools = true;
        //一个vue实例,接管上面的容器
        new Vue({
            el: '.root',
            data: {
                name: '陈皮',
                sex: '男'
            }

        })
    </script>

五、理解MVVM模型

MVVM含义

  • M:(模型Model) 指data中的数据

  • V:(视图View) 指容器,模板代码(html+vue语法的代码)

  • VM:(视图模板ViewModel)其实就是Vue实例,连接M与V的中间人

特别注意

  • data中所有的属性,最后都出现在vm身上
  • vm身上所有的属性和Vue原型上所有的属性,在Vue模板中康都可以直接使用,注意函数都不用写()
<div id="root">
        <h2>你好,{{name}},我住在{{address}}</h2>
        <h2>测试1{{_t}}</h2>
        <h2>测试2{{$refs}}</h2>
        <h2>测试3{{ $options }}</h2>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm=new Vue({
            el: '#root',
            data: {
                name: '小陈',
                address: '新余'
            }
        })
        console.log(vm);
    </script>

六、Object.defineproperty()方法回顾

<script>
        let num=10;
        let person={
            name: '小陈',
            sex: '男'
        }
        
        Object.defineProperty(person,'age',{
            //value: num,
            enumerable: true,//age可以枚举
            //writable: true,//值可以修改
            configurable: true,//控制属性是否可以删除,默认为false
            get: function(){
                console.log('每次查询age都会调用get函数');
                return num;
            },
            set(value){
                console.log('每次修改age都会调用set函数');
                num=value;
            }

        });
       // person.age=11;//writable默认为false,无法修改
        console.log(person);
        for (const key in person) {
           console.log(key+"=>"+person[key]);//无法枚举出age,enumerable默认为false
        }

    </script>

思考:js如何在obj2中操作obj1的属性?

<script>
        let obj1={name: 'chen'};
        let obj2={address: '江西'};
        //思考:如何在obj2中操作obj1中的属性内?
        Object.defineProperty(obj2,'name',{
            get(){
                return obj1.name;
            },
            set(value){
                obj1.name=value;
            }
            
        })
    </script>

七、Vue中的数据代理

  • vue实例中并没有data属性,vue实例是把data中的属性放到_data对象中,然后我们访问name或者address都是调用getter从_data属性中拿的,我们修改name,都是调用setter修改_data中的属性
  • Vue中数据代理:通过vm对象来代理data对象中属性的操作(读写)
  • 同之前Object.defineProperty()一样,把data对象中所有属性添加到vm上,为每一个添加到vm上的属性都指定一个setter和getter.在getter和setter内部操作data中对应的属性.
  • vue数据代理的好处是更加方便操作data中数据,其实没有数据代理,我们访问data中的属性就是vm._data.name,也可以实现.
    <div id="root">
        <h3>hello,{{name}},在{{address}}</h3>
    </div>
    <script>
        
        const vm = new Vue({
            el: '#root',
            data: {
                name: '小陈啊',
                address: '江西'
            }
        });
        console.log(vm);
    </script>

八、Vue的事件

事件的基本使用

  • 使用v-on:xxx 或者简写为@xxx,绑定事件,其中xxx是事件名称(事件类型)
  • 事件的回调需要配置在methods对象中,最终方法会在vm对象上
  • methods中配置的函数,不要用箭头函数,否则this就不是指向vm或者组件实例对象了,而是指向window
  • methods中配置的函数,都是被Vue所管理的函数,this的指向都是vm或者组件实例对象
  • @click="fn"和@click="fn($event)"效果是一致的,但是后者可以传参数
<div id="root">
        <h3>hello,{{name}}</h3>
        <button v-on:click="fn1">我是按钮1</button>
        <button @click="fn2">我是按钮2</button>
        <button @mouseover="fn3">我是按钮3</button>
        <button class="btn4" @click="fn4(333,$event)">我是按钮4,带了参数</button>
    </div>
    <script>
        const vm=new Vue({
            el: '#root',
            data: {
                name: 'chen',
                fn3:()=>{
                    alert('我是方法3,写在data对象中,会被vue数据代理');
                    console.log(this);//箭头函数得到的this是window
                }
            },
            methods: {
                fn1: function(){
                    alert('我是方法1');
                    console.log(this);//普通函数里面的this是Vue实例
                },
                fn2(){
                    
                    alert('我是方法2');
                },
                fn4(num,event){//event是空,因为btn中没有用$event来占位置
                    console.log(num);
                    console.log(event.target.innerText);//得到按钮的值
                    console.log(event.target.getAttribute('class'));//得到按钮的类
                }
            }
        });
    </script>

事件修饰符

  1. @click.prevent:点击超链接会先出弹窗,然后执行超链接默认事件跳转,加了@click.prevent则会阻止默认事件
  2. @click.stop(加到儿子头上):在子盒子中加会阻止冒泡事件
  3. @click.once:事件只会执行一次
  4. @click.capture(加到父亲头上):使用事件的捕获机制,先弹出父亲事件,再子
  5. 修饰符可以连续写: @click.prevent.once
  6. 键盘事件总结
    • 回车:enter
    • 退出:esc
    • 空格:space
    • 换行: tab(结合keydown使用)
    • @keyup:按键抬起时才会触发
    • @keydown:按下就触发
    • 上:up,下down
<div id="root">
        <button @click.once="fn">我是按钮</button>
        <h2><a href="http://www.randycout.com" @click.prevent.once="fn">我的博客网址</a></h2>
        <div class="parent" @click.capture.once="fn1"><!--只会执行一次,并且执行捕获模式-->
            我是你爸爸
            <div class="son" @click.stop="fn2">
                我是儿子
            </div>
        </div>
        <input type="text" placeholder="练习键盘事件" @keyup.space="fn3">
    </div>
    <script>
        new Vue({
            el: '#root',
            data: {

            },
            methods: {
                fn(){
                    alert('点了超链接');
                },
                fn1(){
                    alert('爸爸盒子的弹框');
                },
                fn2(){
                    alert('儿子的弹框');
                },
                fn3(e){
                    alert(e.target.value);
                }
            }
        })
    </script>

姓名案例插值与方法的写法

<div id="root">
        姓:<input type="text" v-model:value="firstName"><br><br>
        名:<input type="text" v-model="lastName" ><br><br>
        <!-- 1.插值写法 -->
        姓名:<span>{{firstName.slice(0,3)+"--"+lastName}}</span><br><br>
        姓名:<span>{{firstName}}--{{lastName}}</span><br><br>
        <!-- methods写法1,作为事件的回调实现 -->
        全名<button @click="fn">点击出全名</button><br><br>
        <!-- methods写法2,插值语法,自己调用事件,一定要加(),不加就会展示函数体,加()是要返回值-->
        全名<span>{{fn1()}}</span><br><br>
    </div>
    <script>
        new Vue({
        el: '#root',
        data: {
            firstName: '张',
            lastName: '三'
        },
        methods: {
            fn(){
                alert(this.firstName+'--'+this.lastName);
            },
            fn1(){
                console.log('-----');
                return this.firstName+'--'+this.lastName;
            }
        }
    })
    </script>

计算属性

  1. 定义:要用的属性不存在,要通过已有的属性计算得来
  2. 原理:底层借助Object.defineProperty()方法提供的getter和setter
  3. get函数什么时候执行:
    • 初次读取时会执行一次
    • 当依赖的数据发生变化时会再次调用
  4. 优势: 与methods实现相比,内部有缓存机制,效率更好,调式方便
  5. 备注
    • 计算属性最终会出现再vm上,直接用插值语句使用即可
    • 如果计算属性要被修改,那一定要写setter函数,且setter函数要引起计算式依赖的数据发生变化
<div id="root">
        姓:<input type="text" v-model="firstName"><br><br><br>
        名:<input type="text" v-model="lastName"><br><br><br>
        姓名(完整写法):<span>{{fullName}}</span><br><br><br>
        姓名(简写法):<span>{{fullName2}}</span><br><br><br>
    </div>
    <script>
        let a=3;//a脱离了vue实例,修改了它也无法检测到,所以要通过现有的属性,脱离的不算
        const vm = new Vue({
            el: '#root',
            data: {
                firstName: '张',
                lastName: '三'
            },
            computed:{//计算属性,现有的属性加工处理,得到的新属性,这个属性再vm身上,但不在_data对象里面
                //完整写法
                fullName: {
                    //当有个读取fullName时就会调用get,且返回值就是fullName的值
                    //get什么时候被调用?1.初次读取fullName时 2.所依赖的数据发生修改时调用
                    //计算属性做了缓存,如果数据没有发生变化,多次调用此属性,它会从缓存中读取,期间get不会被调用
                    get(){//不能写成箭头函数,不然this就是window了
                        console.log('get函数被调用');
                        return this.firstName+'--'+this.lastName+a;
                    },
                    //只要修改fullName,就会调用set函数,修改后的值保存为fullName的值
                    set(value){//不配set表示不用修改
                        console.log('set函数被调用',value);
                        const arr=value.split('--');
                        this.firstName=arr[0];
                        this.lastName=arr[1];
                        
                    }
                },
                //计算属性的简写(1.没有setter,只要getter方法 2.fullName2不是方法,调用时不能带() 2.function里面写set内容)
                fullName2: function(){
                    console.log('get函数被调用');
                    return this.firstName+"--"+this.lastName;
                }

            }
        });
        
    </script>

天气案例

<div class="root">
        <h2>今天天气{{weather}}</h2>
        <button @click="change">切换天气</button>
        <button @click="ishot=!ishot">切换天气</button>
    </div>
    <script>
       const vm= new Vue({
            el: '.root',
            data: {
                ishot : true
            },
            methods: {
                change(){
                    this.ishot=!this.info;
                }
            },
            computed: {
                weather(){
                    return this.ishot ? '炎热':'凉爽';
                }
            },
        })
    </script>

监视属性watch

  1. 当被监视的属性发生变化后,回调函数会自动调用,进行相关操作
  2. 监视的属性必须存在,才能进行监测
  3. 监视的两种写法
    • new Vue时传入watch配置
    • 通过vm.$watch('dataName',{})进行监视
<div class="root">
        <h2>今天天气{{weather}}</h2>
        <button @click="change">切换天气</button>
    </div>
    <script>
       const vm= new Vue({
            el: '.root',
            data: {
                ishot: true,
            },
            methods: {
                change(){
                    this.ishot=!this.ishot;
                }
            },
            computed: {
                weather(){
                    return this.ishot ? '炎热': '凉爽';
                }
            },
            watch: {
                ishot: {
                    immediate: true,//初始化时会调用1次
                    handler(newValue,oldValue){//当ishot变化时会调用handler
                        console.log(newValue+'---'+oldValue);
                       // return this.ishot ? '炎热':'凉爽';
                    }
                },
                // weather: {//计算属性也可以监听
                //     immediate: true,
                //     handler(newValue,oldValue){
                //         console.log(newValue);
                //         console.log(oldValue);
                //     }
                // }
            }
        });
        //监视的另一种写法
        vm.$watch('weather',{//这里面的写法wather:{..}一模一样
            immediate: true,
            handler(n,o){
                console.log(n+'---'+o);
            },
        })
    </script>

深度监视

  1. vue中的watch默认不检测对象内部值的变化(只看最外一层)
  2. 配置deep:true可以监测对象内部值的变化(多层)
  3. vue自身可以监测对象内部值的改变,但是vue提供的watch默认不行
  4. 使用watch时根据数据的具体结构,决定是否采用深度监视
<div class="root">
        <h2>监测数据a:{{num.a}}</h2>
        <button @click="num.a++;">增加a</button>
        <h2>监测数据b:{{num.b}}</h2>
        <button @click="num.b++;">增加b</button>
        <h2>监测数据d:{{num.c.d}}</h2>
        <button @click="num.c.d++;">增加d</button>
        
    </div>
    <script>
        const vm=new Vue({
            el: '.root',
            data: {
                num: {
                    a:10,
                    b:20,
                    c:{
                        d:100
                    }
                }
            },
            watch:{
                'num.a':{//可以监测到a,完整写法
                    immediate:true,
                    handler(n,o){
                        console.log('a的值:'+n+'---'+o);
                    }
                },
                'num.b':function(n,o){//当不需要其他配置,只要handler时可以简写
                    console.log('b的值:'+n+'---'+o);
                }
                
            }
        });
        //如何监测到num的变化呢?
        // vm.$watch('num',{
        //     deep: true,
        //     handler(n,o){
        //         console.log('num值发生变化');
        //     }
        // });
        vm.$watch('num.a',function(n,o){//简写
            console.log('a值变了哦');
        })

    </script>

computed与watch的区别

  1. computed能完成的功能watch都能完成
  2. watch能完成的功能,computed不一定能完成,比如,watch可以进行异步操作
  3. 备注
    • 所有被Vue管理的函数,最好写成普通函数,这样this指向的才是vm或者组件实例对象
    • 所有不能被vue管理的函数(定时器的回调函数,ajax的回调函数,promise的回调函数),最好写成箭头函数,这样this的指向才是vm或者组件实例
<div class="root">
        姓:<input v-model="firstName"><br><br><br>
        名:<input v-model="lastName"><br><br><br>
        全名:<span>{{fullName}}</span>
    </div>
    <script>
        new Vue({
            el: '.root',
            data: {
                firstName: '陈',
                lastName: '志斌',
                fullName: '陈-志斌'
            },
            watch: {
                firstName:function(newValue){
                    this.fullName=newValue+'-'+this.lastName;
                },
                lastName:{
                    handler(value){
                        setTimeout(() => {//这里一定要写成箭头函数,时间到了往外找,找到handler层,这里就是vm对象了
                            
                            this.fullName=this.firstName+'-'+value;
                        
                        }, 2000);
                        
                    }
                }
            }

        })
    </script>

九、Vue绑定样式

class样式

  • 写法 :class="xxx" xxx可以是字符串,对象和数组

style样式

  • :style="{fontSize: xxx}" xxx是动态值
  • :style="[a,b]",其中a,b是样式对象
<style>
        .base{
            width: 200px;
            height: 200px;
            margin: 50px;
            padding: 50px;
            border-color: black;
            border: 2em;
            background-color: aqua;
        }
        .one{
            background-color: greenyellow;
        }
        .two{
            border-radius: 50%;
        }
        .three{
            font-size: 50px;
        }
    </style>
</head>
<body>
    <!-- 绑定样式
        1.class样式:
            - 写法 :class="xxx" xxx可以是字符串,对象和数组
        2.style样式
            :style="{fontSize: xxx}" xxx是动态值
            :style="[a,b]",其中a,b是样式对象
    
    -->
    <div id="root">
        <!-- 字符串写法,适用于样式的类名不确定,需要动态指定 -->
        <div class="base" :class="classOne">老大</div>
        <!-- 数组写法,适用于要绑定的样式个数不确定,名字也不确定(三个属性都用上了) -->
        <div class="base" :class="classArr">老二</div>
        <!-- 对象写法,适用于样式个数确定,名字也确定,但要动态决定用不用 -->
        <div class="base" :class="classObj">老三</div>


        <!-- 绑定style样式,对象写法 key不能瞎写,要是css中存在的 -->
        <div class="base" :style="styleObj">老四</div>
        <!-- 数组写法 -->
        <div class="base" :style="styleArr">老五</div>
        <div class="base" :style="[{'fontSize':'90px'},{'color':'red'}]">老六</div>
    </div>
    <script>
        new Vue({
            el: '#root',
            data: {
                classArr:['one','two','three'],//数组写法
                classOne: 'one',//字符串写法
                classObj:{//对象写法
                    one: false,
                    two: false,
                    three: true
                },
                styleObj:{
                    backgroundColor: 'orange'
                },
                styleArr:[
                    {
                        fontSize: '60px'
                    },
                    {
                        color: 'blue'
                    }
                ]
            }
        })
    </script>

十、条件渲染

v-if(完全把组件移除):

  • v-if="表达式"
  • v-else-if="表达式"
  • v-else
  • 适用于切换频率较低的场景,不展示的dom元素直接删除,v-if可以和v-else-if,v-else一起使用,但要求结构不能被打乱

v-show

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

  • 适用于切换频率较高的场景,不展示的dom元素不会被删除,只是用display:none隐藏了

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

<div id="root">
        <h3>数字是{{num}}</h3>
        <button @click="num++">点我加1</button><br><br><br>
        <div class="box" v-show="num==3">
            大盒子
        </div>
        <div class="box" v-if="num==1">
            num=1我就显示
        </div>
        <div class="box" v-else-if="num==2">
            num=2我就显示
        </div>
        <div class="box" v-else>
            v-else不需要加条件
        </div>
        <template v-if="num<3">
            <h2>num小于3时我会显示</h2>
        </template>
        <template v-else-if="num>=3&&num<=6">
            <h2>3<=num<=6时我会显示</h2>
        </template>
        <template v-else>
            <h2>num>6时我会显示</h2>
        </template>
    </div>
   
    <script>
        new Vue({
            el: '#root',
            data: {
                num: 0
            }
        })
    </script>

十一、列表渲染

v-for指令总结

  • 语法 v-for="(item,index) in xxx" :key="yyy" ,其中in可以改为of
  • 可遍历数组,对象,字符串和指定次数
<div class="root">
        <ul>
            <!-- 遍历数组对象 -->
            <li v-for="(p,index) in persons">
                {{p.name}}--{{p.age}}--{{index}}
            </li> 
        </ul>
        <ul>
            <!-- 遍历对象 -->
            <li v-for="(value,key) in student">
                {{key}}---{{value}}
            </li>
        </ul>
        <ul>
            <!-- 遍历字符串 -->
            <li v-for="(s,index) in str">
                {{s}}--{{index}}
            </li>
        </ul>
        <ul>
            <!-- 遍历数字 -->
            <li v-for="(num,index) in 6">
                {{num}}--{{index}}
            </li>
        </ul>
    </div>
    <script>
        new Vue({
            el: '.root',
            data: {
                str: 'abcefdfd',
                student:{
                    'sno': '001',
                    'sname': '小陈',
                    'sage': 20

                },
                persons:[
                    {
                        'no': '001',
                        'name': '张三',
                        'age': 18
                    },
                    {
                        'no': '002',
                        'name': '李四',
                        'age': 19
                    },
                    {
        
                        'no': '003',
                        'name': '王五',
                        'age': 20
                    }
                ]
            }
        })
    </script>

列表渲染key详解(vue中key内部原理)

  1. 虚拟DOM中key的作用

    key时虚拟Dom对象的标识,当数据发生变化时,Vue会根据新数据生成新的虚拟DOM, 随后Vue进行新虚拟DOM与旧的虚拟DOM的差异比较,比较规格如下

  2. 新旧虚拟DOM比较规则

    • 旧虚拟DOM中找到了与新虚拟DOM相同的key
      • 若虚拟DOM中内容没变,直接使用之前的真实DOM
      • 若虚拟DOM内容变了,则长生新的真实DOM,随后替换界面之前的真实DOM
    • 虚拟DOM中没有找到与新虚拟DOM相同的key:创建新的真实DOM,随后渲染到页面
  3. 用index作为key可能引发的问题

    • 若对数据进行逆序添加,逆序删除等破坏顺序操作: 会产生没有必要的真实DOM更新,效率低下
    • 如果结构中还包含输入类的DOM会产生错误的DOM更新,界面会出问题
  4. 如何选择key

    • 最好使用每条数据的唯一标识作为key,即数据主键
    • 如果不存在对数据的逆序操作,则使用index作为key没有问题
    • 若没有指明key,则系统默认会用index作为key
<div id="root">
        <ul>
            <li v-for="(p,index) in persons" :key="index">
                {{p.name}}--{{p.age}}
                <input type="text">
            </li>
        </ul>
        <button @click="add">在头部添加老刘</button>
    </div>
    <script>
        new Vue({
            el: '#root',
            methods: {
                add(){
                    const p={'no': '004','name': '老刘','age': 22};
                    this.persons.unshift(p);//在头部添加老刘
                }
            },
            data: {
                persons:[
                    {
                        'no' : '001',
                        'name': '张三',
                        'age': 19
                    },
                    {
                        'no': '002',
                        'name': '李四',
                        'age': 20
                    },
                    {
        
                        'no': '003',
                        'name': '王五',
                        'age': 21
                    }
                ]
            }
        })
    </script>

列表排序案例

<div class="root">
        计算属性查找:<input type="text" v-model="sel"><br><br><br>
        侦听方法查找:<input type="text" v-model="key"><br><br><br>
        <ul>
            <!-- 计算属性查询到的人 -->
            <li v-for="p in selPersons">
                {{p.name}}--{{p.age}}
            </li> 
        </ul>
        <ul>
            <!-- 侦听方法查询到的人 -->
            <li v-for="p in personsList">
                {{p.name}}--{{p.age}}
            </li> 
        </ul>
     <hr>
        <ul>
            <!-- 遍历数组对象 -->
            <li v-for="(p,index) in persons">
                {{p.name}}--{{p.age}}
            </li> 
        </ul>
        
    </div>
    <script>
        const vm=new Vue({
            el: '.root',
            data: {
                sel: '',
                key: '',
                personsList: {},
                persons:[
                    {'no': '001','name': '张三','age': 18 },
                    {'no': '002','name': '李四','age': 18 },
                    {'no': '003','name': '王五','age': 19 },
                    {'no': '004','name': '王三','age': 20 },
                    {'no': '005','name': '李五','age': 21 },
                ]
            },
            computed: {//用计算属性查找
                selPersons(){
                    return this.persons.filter(p=>{
                        return p.name.indexOf(this.sel) !=-1;
                    })
                    
                }
            },
            watch: {
                // key(val){//监听key属性(简写)
                //     this.personsList=this.persons.filter(function(p){
                //         return p.name.indexOf(val)!=-1;
                //     })
                // }
                key: {
                    immediate: true,//初始数值没变的时候先执行一波handler
                    handler(val){
                        this.personsList=this.persons.filter(p=>{//p是每一个对象,返回为true就筛选进数组,最后得到的是数组
                            return p.name.indexOf(val)!=-1;
                        })
                    }
                }
            }
        });

    </script>

十二、Vue数据监视原理

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

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

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

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

    • 如需要添加的属性做响应式,要使用如下API

      Vue.set(target.propertyName/index,value)
      vm.$set(target.propertyName/index,value)
      
  3. 如何监测数组中的数据?

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

    • 调用原生对应的方法对数组进行更新
    • 重新解析模板,进而更新页面
  4. 在Vue修改数组中的某个元素一定要用如下方法

    • push():末尾添加一个元素
    • pop():删除末尾一个元素
    • unshift():在数组头部添加一个元素
    • shift():删除头部一个元素
    • splice():替换元素
    • reverse():反转元素
    • sort():数组排序
  5. Vue.set()和vm.$set()不能给vm或者vm的根数据对象添加属性!!!!

    不能Vue.set(vm._data,'key','value')

 <div class="root">
        <h3>学生姓名:{{student.name}},学生年龄:{{student.age}}</h3>
        <h3>学生爱好:{{student.hobbit}}</h3>
        <button @click="changeOneHobbit">修改一个爱好</button> <button @click="changeAllHobbit">修改所有爱好</button>
        <h3>学生朋友:{{student.friends}}</h3>
        <button @click="changeFriend1">修改朋友不展示</button> <button @click="changeFriend2">修改一个朋友展示</button>
        <h3>学生班级:{{student.clazz}}</h3>
        <button @click="addAdr">增加班级地址</button>
        <h2>人群:{{persons}}</h2>
        <button @click="changePerson1">修改人不展示</button>
        <button @click="changePerson2">修改人展示</button>
        <button @click="addAge">给第一个人增加人群年龄</button>
        <h3>{{student.father}}</h3>
        <h3>{{student.mother}}</h3>
        <button @click="addParent">给学生加双亲</button>
    </div>
    <script>
        
        const vm=new Vue({
            el: '.root',
            data: {
                student: {
                    name: '小陈',
                    age: '18',
                    hobbit: ['看书','打游戏','敲代码'],
                    friends:[
                        {'name':'张三','age':18},
                        {'name':'李四','age':19}
                    ],
                    clazz:{
                        'name':'计科3班',
                        'num':45,
                        'room': 307
                    }
                },
                persons:[
                    {'name':'大毛','sex':'男'},
                    {'name':'二毛','sex':'女'},
                ]
            },
            methods: {
                changeOneHobbit(){
                   this.student.hobbit[0]='阅读';//页面展示不奏效,虽然数据在vm.student.hobbit中改好了
                },
                changeAllHobbit(){
                  // this.student.hobbit=['唱','跳','rap','篮球'];//页面展示奏效,因为hobbit有get和set方法
                 //  this.student.hobbit.push('唱歌');//正确操作数组的方式,用那7个方法,尾部加唱歌
                 //  this.student.hobbit.shift('看书');//头部删除看书
                 //  this.student.hobbit.sort();
                    this.student.hobbit.unshift('摸鱼');//头部加摸鱼
                    this.student.hobbit.splice(1,2,'玩NBA2KOL');//splice(起始下标,数量,新增元素),splice(起始下标,数量)//删除元素
                    this.student.hobbit.reverse();//逆序输出
                },
                changeFriend1(){
                    this.student.friends[0]={'name':'老王','age':'28'}//数据改了,但是vue监听不到,要等下次vue解析模板时才会显示
                },
                changeFriend2(){
                    this.student.friends[0].name='老陈';//同样也监测不到,name和age都没有set和get方法
                    this.student.friends[0].age=38//
                },
                changePerson1(){
                    this.persons[0]={'name':'熊大','sex':'男'};//不奏效
                },
                changePerson2(){//坑点:如果先点了changePerson1,则数据改变后没有get和set,这会导致changePerson2也无法被vue监测到
                    this.persons[0].name='熊二'
                    this.persons[0].sex='女'
                },
                addAdr(){//添加响应式地址属性
                    this.$set(this.student.clazz,'adress','上海');
                },
                addAge(){
                    this.$set(this.persons[0],'age',50);
                },
                addParent(){
                   this.$set(this.student,'father','老王');
                   Vue.set(this.student,'mother','小花');//两种写法都可以
                }
                
            }
        });
    

    </script>

十三、表单数据收集

  1. ,则v-model收集的时value值,用户输入的就是value值
  2. ,则v-model收集的是value值,我们要给标签配置value值
  3. [ ] - 没有配置的input的value属性,则收集的就是checked(勾选 or 未勾选,是个布尔值) - 若配置了value属性 1. v-model的初始值是非数组,那么收集就是checked(布尔值) 2. v-model的初始值是数组,那么收集的就是value组成的数组
  4. v-model的三个修饰符
    • lazy: 失去焦点再来收集数据
    • number: 输入字符串转为有效数数字
    • trim: 对首尾空格过滤
    • @submit.prevent: 阻止表单默认跳转事件
<div class="root">
        <!--@submit.prevent阻止表单默认跳转事件  -->
        <form @submit.prevent>
            姓名:<input type="text" v-model.trim="user.name"><br><br><br>
            年龄:<input type="number" v-model.number="user.age"><br><br><br>
            性别:
                男<input type="radio" v-model="user.sex" value="man">
                女<input type="radio" v-model="user.sex" value="woman"><br><br><br>
            爱好:
                唱<input type="checkbox" v-model="user.hobbit" value="sing">
                跳<input type="checkbox" v-model="user.hobbit" value="drup">
                rap<input type="checkbox" v-model="user.hobbit" value="rap">
                篮球<input type="checkbox" v-model="user.hobbit" value="basketball"><br><br><br>
            居住过的城市: <select v-model="user.city">
                            <option value="">请选择城市</option>
                            <option value="bj">北京</option>
                            <option value="sh">上海</option>
                            <option value="sz">深圳</option>
                         </select><br><br><br>
                其他:<textarea v-model="user.other"></textarea><br><br><br>
                <button @click="showAll">提交数据</button>
        </form>
    </div>
    <script>
        const vm=new Vue({
            el: '.root',
            data: {
                user:{
                    name: '',
                    age: '',
                    sex: '',
                    hobbit:[],
                    city:'',
                    other: ''
                }
            },
            methods: {
                showAll(){
                   console.log(JSON.stringify(this.user));
                }
            },
        })
    </script>

十四、指令

内置指令

  • v-bind 单向绑定解析表达式,可简写为:
  • v-model 双向数据绑定
  • v-for
  • v-on
  • v-show
  • v-if v-else-if v-else
  • v-text : 会完全替换标签中的文本
  • v-html : 能解析文本中的html标签,不安全,容易导致xxs攻击
  • v-cloak : 没有值,Vue实例创建完毕后会移除此属性,配合css,保证Vue未接管容器时,不会给客户展示{{name}}未解析的内容
  • v-once : 初次解析一次就视为静态内容不会在改变了
  • v-pre : 跳过所在节点的模板解析过程,提高效率
<div class="root">
        <!-- v-text会完全替h2中的文本 -->
        <h2 v-text="name">我在敲代码</h2>
        <!-- v-html能解析文本中的html标签,不安全,容易导致xxs攻击 -->
        <h2 v-html="str">我是h2的文本</h2>
        <!-- v-cloak没有值,Vue实例创建完毕后会移除此属性,配合css,保证Vue未接管容器时,不会给客户展示{{name}}未解析的内容 -->
        <h2 v-cloak>我在{{name}}</h2>
        <!-- v-once指令,初次渲染一次就视为静态内容不会在改变了 -->
        <h2 v-once>初始值:{{num}}</h2>
        <h2>num的值:{{num}}</h2>
        <button @click="num++">num+1</button>
        <!-- v-pre跳过所在节点的编译过程,提高效率 -->
        <h3 v-pre>加了v-pre指令,不会解析num{{num}}</h3>
    </div>
    <script>
        new Vue({
            el: '.root',
            data: {
                name: '练习v-text',
                str: '<h1>我是带h1标签的str</h1>',
                num: 10,
            }
        })
    </script>

十五、自定义指令

  1. 定义语法:

    • 局部指令

      new Vue({//完整写法
          directives: {
              指令名称: {
                      bind(el,binding){//指令与元素成功绑定时调用(解析完毕,未放到页面)
                      },
                      inserted(el,binding){//指令所在元素被插入页面时调用(解析好的模板,放到页面)
                      },
                      update(el,binding){//指令所在模板被重新解析时调用(模板其他元素的改变引起页面重新解析也会被调用)
                      }
               }
          }
       })
      
      new Vue({//简单写法
          directives:{
               指令名称(el,binding){//只有bind和update两个时刻
               }
          }
      })
      
    • 全局指令

      Vue.directive('指令名称',function(el,binding){//简单写法
      })
      
      Vue.directive('指令名称',{//完整写法
          bind(el,binding){
          },
          inserted(el,binding){
          },
          update(el,binding){
          }
      })
      
  2. 备注:

    • 指令是通过函数来处理逻辑,不是靠返回值,不是一个表达式.
    • 指令定义时不用加- ,但是使用时要加v-
    • 指令名如果是多个单词,定义用驼峰oneTwo,使用时v-one-two
 <div class="root">
        <h3>n的值时:{{n}}</h3>
        n的10倍:<span v-big="n"></span><br><br><br>
        n的100倍:<span v-biger="n"></span><br><br><br>
        n的1000倍:<span v-bigest="n"></span><br><br><br>
        n的10000倍并且拿到焦点:<input type="text" v-big-focus="n"><br><br><br>
        <button @click="n++">点我n+1</button><br><br><br>
        
    </div>
    <script>
        Vue.directive('biger',function(el,binding){//全局自定义指令写法(只有bind和update)
            el.innerText=binding.value*100;
        });
        Vue.directive('bigest',{//完整写法
            bind(el,binding){//指令与元素成功绑定时调用
                el.innerText=binding.value*1000;
            },
            inserted(el,binding){//指令所在元素被插入页面时调用
                el.innerText=binding.value*1000;
            },
            update(el,binding) {//指定所在模板结构被重新解析时调用
                el.innerText=binding.value*1000;
            },
        })
        const vm = new Vue({
            el: '.root',
            data: {
                n: 1
            },
            directives:{//局部指令
                big(element,binding){//这样简写只包括bind和update两个时刻,没有更细化的inserted时刻
                   // console.log(element);//<button v-big="n">点我n*10</button>,拿到元素
                   // console.log(binding);//绑定对象,有n的新旧值和表达式
                    element.innerText=binding.value*10;
                },
                bigFocus:{//使用是的名称: v-big-focus
                    bind(element,binding){//解析完模板,还没有放到真实DOM上
                        element.value=binding.value*10000;
                    },
                    inserted(element,binding){//解析完模板,并放到真实DOM放在页面
                        element.focus()
                    },
                    update(element,binding){//重新解析模板时调用
                        element.value=binding.value*10000;
                    }
                }
            }
        });
        
    </script>

十六、Vue生命周期

  1. 什么是生命周期?

    是Vue实例从创建到销毁这一生中各个关键时刻给我们用的函数(vue实例自己会调用,不要程序员手动调用)

  2. 生命周期函数名不可更改,但里面的业务逻辑我们可以编写,共8组,4对

    • beforeCreate() : 数据监测,数据代理初始化之前(有vm,没有_date属性,没有响应式setter和getter,无法访问data,methods)
    • created() : 数据监测,数据代理初始化完毕后(可以访问data,methods)
    • beforeMount() : Vue已经解析好了模板,并生成虚拟DOM(放入内存),但是并没有放到页面中,页面还是未经编译的DOM,此时对DOM的操作最终都不奏效
    • mounted() : Vue将虚拟的DOM(内存)转为真实的DOM(vm.$el()留了一份真实DOM),并插到页面中了,页面展示的也是已经解析完毕的DOM
    • beforeUpdate() : data发生变化时会触发,这个点数据是新的,但是页面是旧的,是唯一一个数据和页面不一致的时刻
    • updated() : 新的虚拟DOM与旧的虚拟DOM进行比较完毕后,最终页面更新,到这个时刻,数据与页面保持一致
    • beforeDestroy() : 当vm调用vm.$destroy()会触发,此时vm的data,methods和指令等等都可以用,但是页面不进行响应更新了
    • destroyed()
  3. 生命周期图

    • mounted(): 到这一时刻,Vue的初始化工作完成,此时可以在这进行发送网络请求,开启定时器,订阅消息,绑定自定义事件等
    • beforeDestroy(): 销毁之前进行关闭定时器,取消订阅消息,解绑自定义事件等收尾工作.

end
  • 作者:  molimark (联系作者)
  • 发表时间:  2022-08-11 21:23
  • 版权声明:  自由转载-非商用-非衍生
  • Comments

    留言