vue生命周期函数介绍

  • 引入场景: 呈现文字透明渐隐的效果
......
<body>
    <div id="container">
        <!--值必须写成对象式,在js中,若键值对名称一样,则可以简写-->
        <h3 :style="{opacity}">欢迎!</h3>
    </div>
    
    <script type="text/javascript">
        
        var vm = new Vue({
            'el':'#container',
            data:{
                opacity:1, // 设置不透明度
            },
        });
    
    </script>
</body>
  • 解决方式一:在vm外部使用计时器
......
<body>
    <div id="container">
        <h3 :style="{opacity}">欢迎!</h3>
    </div>
    
    <script type="text/javascript">
        
        var vm = new Vue({
            'el':'#container',
            data:{
                opacity:1,
            },
        });
        
        setInterval(()=>{ // 使用计时器成功解决这个问题
            vm.opacity -= 0.01
            if(vm.opacity <= 0) vm.opacity = 1 // js简写
        },16)
    
    </script>
</body>
  • 如果一定要把逻辑写vue里面,该怎么办
    以下代码示例是一个死循环,后果就是耗尽CPU
......
 <body>
    <div id="container" v-cloak>
        <h3 :style="{opacity}">欢迎!</h3>
        {{change()}} <!--调用这个方法-->
    </div>
    
    <script type="text/javascript">
        
        var vm = new Vue({
            'el':'#container',
            data:{
                opacity:1,
            },
            methods:{
                change(){
                    setInterval(()=>{ // 把之前定时器的逻辑放里面
                        console.log('123')
                        this.opacity -= 0.01
                        if(this.opacity <= 0) vm.opacity = 1
                    },16)
                }
            }
        });
        

    </script>
</body>
......

- 效果:页面无限生成计时器,直至崩溃...

    - 由于定时器定时修改data的值,而vue一旦检测到data数据的变动,就重新解析模板

    - 而数据是一直发生变化的,所以vue就一直解析模板,这个动作无限循环下去...
  • 使用vue生命周期函数"mount()"来解决这个问题
    当页面加载完毕以后(vue挂载完毕),会自动调用 mount
......
<body>
    <div id="container" v-cloak>
        <h3 :style="{opacity}">欢迎!</h3>
    </div>
    
    <script type="text/javascript">
        
        var vm = new Vue({
            'el':'#container',
            data:{
                opacity:1,
            },
            mounted(){ // 页面加载完毕以后,会被调用一次
                setInterval(()=>{
                    this.opacity -= 0.01
                    if(this.opacity <= 0) vm.opacity = 1
                },16)
            }
        });
    
    
    </script>
</body>
......

- 由于mount只被调用一次,所以不会无限生成计时器
  • 小结
- 生命周期

    - 生命周期回调函数,生命周期函数,生命周期钩子

    - 作用: vue在关键时刻帮我们调用的一些特殊名称的函数

    - 钩子函数的名称不可更改,具体逻辑要自己实现

    - 钩子函数的this,指向vue实例/组件对象

Vue生命周期原理解析

  • 引入场景
......
<body>
    <div id="container" v-cloak>
        <h2>当前n的值是:{{n}}</h2>
        <button type="button" @click="add">计算</button>
    </div>
    
    <script type="text/javascript">
        
        var vm = new Vue({
            'el':'#container',
            data:{
                n:1
            },
            methods:{
                add(){
                    this.n++;
                }
            },
            beforeCreate(){
                console.log('beforeCreate')
                console.log(this)
                debugger // 断点打在这边,观察上面的this(vm实例)
            }
        });
    
    </script>
</body>
......

- 此时,vm是没有 _data属性的

Vue 生命周期小结

- vm实例的生命周期(部分周期未展示)

    - 将要创建: beforeCreate()
    - 创建完毕: created()
    - 将要挂载: beforeMount()

    - 挂载完毕: mount() # 最常用,比如页面加载完成以后,立即发送ajax

    - 将要更新: beforeUpdate()
    - 更新完毕: update()

    - 将要销毁: beforeDestroy() # 常用,比如删除计时器,取消订阅消息...

    - 销毁完毕: destroy()
  • 之前定时器的示例,若要停止计时器,可以这干
......
<body>
    <div id="container" v-cloak>
        <h3 :style="{opacity}">欢迎!</h3>
        <!--新增按钮-->
        <button type="button" @click="stop">停止计时器</button>
    </div>
    
    <script type="text/javascript">
        
        var vm = new Vue({
            'el':'#container',
            data:{
                opacity:1
            },
            methods:{
                stop(){ // 点击按钮,停止计时器
                    clearInterval(this.timer)
                }
            },
            mounted(){ // 页面加载完成以后,就启动计时器
                this.timer = setInterval(()=>{ // 临时给vm实例增加一个timer属性,接收计时器的返回值,以便停止它
                    console.log('计时器开始')
                    this.opacity -= 0.01
                    if(this.opacity <= 0) vm.opacity = 1
                },16)
            }
        });
        
    </script>
</body>
  • 可以使用vm生命周期函数 destroy()替换上面 stop() 的逻辑
......
methods:{
    stop(){
        // clearInterval(this.timer)
        this.$destroy() // 不仅停止了计时器,整个vm实例也被删除
    }
},
......
- 上述写法其实是有缺陷的,为了停止计时器,把整个vm实例删除,有点极端
  • 现在,使用 beforeDestroy 停止计时器是最好的选择
......
<script type="text/javascript">
            
    var vm = new Vue({
        'el':'#container',
        data:{
            opacity:1
        },
        methods:{
            stop(){
                this.$destroy() // 摧毁之前,会调用 beforeDestroy
            }
        },
        mounted(){
            this.timer = setInterval(()=>{
                console.log('计时器开始')
                this.opacity -= 0.01
                if(this.opacity <= 0) vm.opacity = 1
            },16)
        },
        beforeDestroy(){
            clearInterval(this.timer) // 被调用
        }
    });
    


</script>
  • vue生命周期最终小结
- 常用的生命周期钩子

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

    - beforeDestroy: 清楚定时器,解绑自定义事件,取消订阅消息等(收尾工作)

- 关于摧毁vue实例

    - 销毁后借助vue开发者工具看不到任何信息

    - 销毁后自定义事件会失效,但原生DOM事件依然有效

    - 一般不会在 beforeDestroy 操作数据,因为即便操作了,也不会触发更新流程

VUE created与mounted区别

  • created mounted

    • created:在模板渲染成html前调用,即通常初始化某些属性值,然后再渲染成视图

    • mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作

    • 实质就是模板渲染前/模板渲染后的区别

模块

  • 定义: 一个个的js文件

组件

  • 定义: 实现局部(特定)功能效果的代码集合(html(片段)/css/js/image/mp4......)

  • 好处: 别人拿过去直接用,不必再一个个修改css/js......

  • 特点: 组件里面可以再包含组件

- html 文档

    - header 组件
    - middle 组件
        - left 组件
        - right 组件
    - footer 组件


- 单文件组件 和 非单文件组件

    - 单文件组件(一个文件里面只有一个组件,实战用得最多): filename.vue

    - 非单文件组件(一个文件里面有多个组件)

组件demo演示

骨架如下,没有问题
......
<body>
        <div id="container">
            <h2>学校名称: {{schoolName}}</h2>
            <h2>学校地址: {{address}}</h2>
            <hr >
            <h2>学生姓名: {{studentName}}</h2>
            <h2>学生年龄: {{age}}</h2>
        </div>
        
        <script type="text/javascript">
            
            var vm = new Vue({
                'el':'#container',
                data:{
                    schoolName:'第一学校',
                    address:'海淀区',
                    studentName:'JimGreen',
                    age:18
                },
            });
        
        </script>
    </body>
  • 局部组件demo演示
......
<body>
    <div id="container">
        <xuexiao></xuexiao> <!--应用自定义组件标签-->
        <hr>
        <xuesheng></xuesheng> <!--应用自定义组件标签-->
    </div>
    
    <script type="text/javascript">
    
        var school = Vue.extend({ // 注册组件
            template:`
                <div> // div根元素一定要有,否则报错
                    <h2>学校名称:{{schoolName}}</h2>
                    <h2>地址: {{address}}</h2>
                </div>
            `,
            data(){ // 一定要写成函数式
                return {
                    schoolName:'第一学校',
                    address:'海淀区',
                }
            }
        })
        
        var student = Vue.extend({ // 同理
            template:`
                <div>
                    <h2>学生名称:{{studentName}}</h2>
                    <h2>年龄: {{age}}</h2>
                </div>
            `,
            data(){
                return {
                    studentName:'JimGreen',
                    age:18
                }
            }
        })
        
        var vm = new Vue({
            el:'#container',
            components:{ // 注册局部组件标签
                xuexiao:school,
                xuesheng:student
            }
        });
    
    </script>
</body>
  • 加入点击事件演示
......
<body>
    <div id="container">
        <xuexiao></xuexiao>
        <hr>
        <xuesheng></xuesheng>
    </div>
    
    <script type="text/javascript">
    
        var school = Vue.extend({
            template:`
                <div>
                    <h2>学校名称:{{schoolName}}</h2>
                    <h2>地址: {{address}}</h2>
                    <!--加入点击事件-->
                    <button @click="showSchoolName">提示信息</button>
                </div>
            `,
            ......
            methods:{
                showSchoolName(){
                    alert(this.schoolName)
                }
            }
        })
        
        var student = Vue.extend({
            ......
        })
        
        var vm = new Vue({
            el:'#container',
            components:{
                xuexiao:school,
                xuesheng:student
            }
        });
    
    </script>
</body>
  • 全局组件demo演示
......
<body>
    <div id="container">
        <xuexiao></xuexiao>
        <hr>
        <xuesheng></xuesheng>
        <hello></hello> <!--应用-->
    </div>
    
    <script type="text/javascript">
        
        var hello = Vue.extend({ // 定义全局组件(和局部一样)
            template:`
                <div>
                    <h2>你好啊~{{name}}</h2>
                </div>
            `,
            data(){
                return {
                    name:'Kate Green'
                }
            }
        })
        Vue.component('hello',hello) // 不一样的地方
    
        var school = Vue.extend({
            ......
        })
        
        var student = Vue.extend({
            ......
        })
        
        var vm = new Vue({
            el:'#container',
            components:{ // 这里并没有注册 hello组件
                xuexiao:school,
                xuesheng:student
            }
        });
    
    </script>
</body>
  • 小结
- Vue中使用组件的三大步骤

    - 定义组件(创建组件)
    - 注册组件
    - 使用组件(写组件标签)

- 如何定义一个组件

    - 使用 Vue.extend(options)创建

- 如何注册组件

    - 局部注册: 靠 new Vue的时候传入 components选项

    - 全局注册: 靠 Vue.component('组件名',组件)

- 编写组件标签

    - <school></school>

组件的命名

  • 一个单词组成

    • 首字母小写: school

    • 首字母大写: School

  • 多个单词组成

    • kebab-case命名: my-school

    • camelCase命名: MySchool(需要vue脚手架支持)

  • 备注

    • 组件命名尽可能回避HTML中已有的元素名称,例如: h2/H2 都不行

    • 可以使用 name 配置项指定组件在开发者工具中呈现的名字(仅仅定义在开发者工具中的展示名称)

  • 关于组件标签

    • 第一种写法:

    • 第二种写法:

      • 备注: 不使用脚手架时,会导致后续组件不能渲染
  • 一个简写方式

    • const school = Vue.extend(options)

    • const school = options

vue组件的嵌套

  • 示例demo: 子组件只能写在父组件的模板中
......
<body>
    <div id="container">
        <xuexiao></xuexiao> <!--父组件-->
        <hr>
    </div>
    
    <script type="text/javascript">
        
        // 定义子组件
        var student = Vue.extend({
            template:`
                <div>
                    <h2>学生名称:{{studentName}}</h2>
                    <h2>年龄: {{age}}</h2>
                </div>
            `,
            data(){
                return {
                    studentName:'JimGreen',
                    age:18
                }
            }
        })
        
        // 定义父组件
        var school = Vue.extend({
            template:`
                <div>
                    <h2>学校名称:{{schoolName}}</h2>
                    <h2>地址: {{address}}</h2>
                    <button @click="showSchoolName">提示信息</button>
                    <hr>
                    <student></student> // 把子组件写进来
                </div>
            `,
            name:'mySchool',
            data(){
                return {
                    schoolName:'第一学校',
                    address:'海淀区',
                }
            },
            methods:{
                showSchoolName(){
                    alert(this.schoolName)
                }
            },
            components:{ // 注册子组件
                student
            }
        })
        
        
        
        var vm = new Vue({
            el:'#container',
            components:{
                xuexiao:school, // 注册父组件
            }
        });
    
    </script>
</body>
  • 开发中经常这么干
    • vue实例仅仅只管理一个叫'app'的组件
    • 由app组件管理众多子组件
......
<body>
    <div id="container">
        <app></app> <!--只用app组件标签-->
        <hr>
    </div>
    
    <script type="text/javascript">

        // 定义 hello 组件
        var hello = Vue.extend({
            template:`
                <div>
                    <h2>你好啊~{{name}}</h2>
                </div>
            `,
            data(){
                return {
                    name:'Kate Green'
                }
            }
        })
      
        
        // 定义student组件
        var student = Vue.extend({
            template:`
                <div>
                    <h2>学生名称:{{studentName}}</h2>
                    <h2>年龄: {{age}}</h2>
                </div>
            `,
            data(){
                return {
                    studentName:'JimGreen',
                    age:18
                }
            }
        })
        
        // 定义 school组件,并把student组件包含进去
        var school = Vue.extend({
            template:`
                <div>
                    <h2>学校名称:{{schoolName}}</h2>
                    <h2>地址: {{address}}</h2>
                    <button @click="showSchoolName">提示信息</button>
                    <hr>
                    <student></student>
                </div>
            `,
            name:'mySchool',
            data(){
                return {
                    schoolName:'第一学校',
                    address:'海淀区',
                }
            },
            methods:{
                showSchoolName(){
                    alert(this.schoolName)
                }
            },
            components:{
                student
            }
        })
            
        // 定义app组件,包含 hello组件和school组件
        var app = Vue.extend({
            template:`
                <div>
                    <hello></hello>
                    <school></school>
                </div>
            `,
            components:{
                hello,
                school
            }
        })
        
        var vm = new Vue({
            el:'#container',
            components:{
                app, // vue实例只管理app组件
            }
        });
    
    </script>
</body>

组件注意事项

- 本质是 VueComponent 构造函数,是由 vue.extend() 生成的

- 当我们写组件标签,比如<school/>时,vue会帮助我们创建 组件实例对象
  即 new VueComponent(options)

- 特别注意:每次调用 vue.extend() 时,生成都是全新的 组件实例对象

    - 好比python创建两个类实例对象,这两个实例对象相等么?否...

        class Foo(object):
            pass

        f1 = Foo()
        f2 = Foo()

        print(f1 == f2) # False

- this的指向

    - 在 VueComponent 配置中,this指向 组件实例对象

    - 在 Vue实例中,this指向 vm实例

- 简称

    - vm实例

    - vc实例(组件)

组件vc和vm的区别

- vc相当于vm的小弟,vm有的,vc99%也有

- 1%是'el'配置项,即vm服务于根元素(容器),而vc没有这个功能

js原型对象 review

function Demo(){
	this.a = 1;
	this.b = 2;
}
var d = new Demo()
// 二者相等,是一模一样的东东
console.log('Demo',Demo.prototype) // 构造函数具有 显示原型对象Demo.prototype
console.log('d',d.__proto__) // 实例对象具有 隐式原型对象 d.__proto__

Demo.prototype.x = 100;
console.log(d.x) // 100

一个重要的内置关系

- Vuecomponent.prototype.__proto__ === Vue.prototype

- 为什么要有这种关系? 让vc可以访问到Vue原型的属性和方法
  只要Vue绑定了新的属性,那么vc也有这个新属性