vue2.0-2——组件化思想、生命周期、props(组件参数)、自定义事件(emit、v-model、sync)插槽(slot,复用性)、自定义指令(复用性)

一、组件化思想:(老知识:函数–> 新知识:组件化)
把一个大的东西,按照一个逻辑或者业务场景,划分成一个一个小的东西,然后拼接起来。
类似函数思想,这样的好处是职责单一
vue2.0-2——组件化思想、生命周期、props(组件参数)、自定义事件(emit、v-model、sync)插槽(slot,复用性)、自定义指令(复用性)_第1张图片

<body>
    <div id="app">div>

    <script>
      // 根组件
      const app = new Vue({
        el: "#app",
        // template 描述当前组件的信息,无template 情况会走el当成组件;有template 情况会覆盖el;如下图
        // vue2 的时候 必须要有一个 根节点 
template: `
{{msg}}
`
, data() { return { msg: "hello", }; }, }); script> body>

vue2.0-2——组件化思想、生命周期、props(组件参数)、自定义事件(emit、v-model、sync)插槽(slot,复用性)、自定义指令(复用性)_第2张图片
注册组件
vue2 的时候 必须要有一个 根节点 div
注意:后续自己注册组件时不需要el了

<body>
    <div id="app">
		
		<foo>foo>
	div>

    <script>
    // 后期学习 .vue 单文件组件,会给我们编译成对应的template,无命名规范问题所在了。
    
	// 为什么data一定写成函数形式?(用对象会发生引用类型的问题)
 		// 组件 -》 只是个object来进行描述
        // 定义的话 object.data ={name:123,count:123}
        // 引用类型的问题,用函数的话每次都会返回一个新的对象,这样就能保证对应一个组件对应一个独立的data,这样一个组件的更改不会影响其他的
      
      // 注册组件 Bar (局部注册)
      const Bar = {
        template: `
            
bar
`
, }; // 注册组件 Foo(全局注册) Vue.component("Foo", { // 注册组件 Bar (局部注册) components 用于注册局部的组件, template 是描述当前这个组件的元素 components: { Bar, }, data() { return { key: value, }; }, // 模板 局部组件Bar只能在局部调用,只能有一个根元素div(vue3就解决了这个限制了) template: `
foo
`
, }); // 使用组件 Foo ,而局部组件Bar会报错,只能在局部调用(全局注册和局部注册的区别) // vue2 的时候 必须要有一个 根节点 // 根组件 const app = new Vue({ el: "#app", // template 描述当前组件的信息 template: `
{{msg}} 用大写 这里调用局部组件会报错
`
, data() { return { msg: "hello", }; }, });
script> body>

二、生命周期(从生命开始到结束的过程)
钩子:在特定的时间内执行这个函数
beforeCreate() // 创建之前调用
created() // 创建完成后调用
beforeMount() // 挂载之前调用
mounted() // 挂载完成后调用
beforeUpdate() // 更新之前调用
updated() // 更新完成后调用
beforeDestroy() // 组件销毁之前调用
destroyed() // 组件销毁之后调用

<body>
    <div id="app">div>

    <script>
// 子组件 Bar
      Vue.component("Bar", {
        template: "
bar
"
, beforeCreate() { // 创建之前调用 console.log("Bar - before - create"); }, created() { // 创建完成后调用 console.log("Bar - created"); }, beforeMount() { // 挂载之前调用 console.log("Bar - before mount"); }, mounted() { // 挂载完成后调用; console.log("Bar - mounted"); }, // 页面发生变化时调用 beforeUpdate() { // 更新之前调用 console.log("Bar - before update"); }, updated() { // 更新完成后调用; console.log("Bar - updated"); }, // v-if 可决定一个组件的销毁 beforeDestroy() { // 组件销毁之前调用; console.log("Bar - before -destroy"); // 这里面可以做一些事情:比如清空事件监听 }, destroyed() { // 组件销毁之后调用; console.log("Bar - destroy"); }, }); // 全局组件 Foo Vue.component("Foo", { components: {}, beforeCreate() { // 创建之前调用 console.log("foo - before - create"); }, created() { // 创建完成后调用 console.log("foo - created"); }, beforeMount() { // 挂载前调用 console.log("foo - before mount"); }, mounted() { // 挂载完成调用; // 当调用此钩子函数时,所有子组件都已经初始化完成 console.log("foo - mounted"); }, // 页面发生变化时调用 beforeUpdate() { // 更新前调用 console.log("foo - before update"); }, updated() { // 更新完成后调用; console.log("foo - updated"); }, // v-if 可决定一个组件的销毁 beforeDestroy() { // 组件销毁之前调用; console.log("foo - before -destroy"); }, destroyed() { // 组件销毁之后调用; console.log("foo - destroy"); }, data() { return { count: 0, }; }, template: `
foo {{count}}
`
, }); // 根组件 app = new Vue({ el: "#app", template: `
{{msg}}
`
, data() { return { msg: "hello", showFoo: false, }; }, });
script> body>

重点:多个组件调用顺序

三、props(组件接收参数)
props 定义当前组件可接收什么参数,和data一样,但props不能被修改;

props基于对象的语法使用以下选项:

1-type:可以是下列原生构造函数中的一种:String、Number、Boolean、Array、Object、Date、Function、Symbol、任何自定义构造函数、或上述内容组成的数组。会检查一个 prop 是否是给定的类型,否则抛出警告。Prop 类型的更多信息在此。
2-default:any
为该 prop 指定一个默认值。如果该 prop 没有被传入,则换做用这个值。对象或数组的默认值必须从一个工厂函数返回。
3-required:Boolean
定义该 prop 是否是必填项。在非生产环境中,如果这个值为 truthy 且该 prop 没有被传入的,则一个控制台警告将会被抛出。
4-validator:Function
自定义验证函数会将该 prop 的值作为唯一的参数代入。在非生产环境下,如果该函数返回一个 false的值 (也就是验证失败),一个控制台警告将会被抛出。

<body>
    <div id="app">div>

    <script>
      // ts -> String
      // function setName(name) {
      //   // name 函数的参数
      // }
      
      Vue.component("Foo", {
        components: {},
        
        props: {
          fooName: {
            type: String,// 定义当前组件传参的类型为String
            default: "foo --- name",// 定义默认值,不传参是显示默认值
             // 校验 , value为用户传过来的值,必须返回一个值查看校验是否成功或失败
            validator: (value) => {
              console.log(value);
              return value === "heihei";
            },
            required: true,// 定义当前fooName是否是必传的
          },
          
        },
        
        data() {
          return {};
        },
        methods: {
          changeProp() {
            console.log("changeProp");
            // this.props.fooName = "123";// this.props内的值不能被修改,会报错
          },
        },

        template: `
            
foo {{fooName}}
`
, }); // 根组件 const app = new Vue({ el: "#app", template: `
{{msg}} // 变量需要绑定:,常量不需要 响应式数据对象需要绑定一下 “:” 警告错误:定义的是string,传的是number
`
, data() { return { msg: "hello", count: 0, }; }, });
script> body>

四、emit(自定义事件类型)
子组件和父组件如何进行交互?通过emit向上发出一个消息

<body>
    <div id="app">div>

    <script>
      // emit
      Vue.component("Foo", {
        components: {},
        data() {
          return {};
        },
        methods: {
          handleClick() {
            console.log("click");

            // 发起一个事件,子向父级发起事件
            //(自定义事件类型,第一个参数是事件名,第二个参数是事件传参)
            this.$emit("heihei", 1);
            // 烤肉串 命名法 ---> aa-bb-cc
            // 驼峰 命名法 ---> aaBbCc
            this.$emit("change-page", 2);// 官方推荐 ---> 烤肉串 命名法
          },
        },

        template: `
            
foo
`
, }); // 根组件 const app = new Vue({ el: "#app", methods: { handleHeihei(v) { console.log(v);//1 console.log("heihei"); }, handleChangePage(v) { console.log(v);//2 }, }, template: `
{{msg}}
`
, data() { return { msg: "hello", }; }, });
script> body>

五、v-model
应用场景:子组件发出来,父组件去更新的(显示)需求

<body>
  <div id="app">div>
  
  
  <script>
    Vue.component("Foo", {
      components: {
      },
      data() {
        return {
        };
      },
      methods: {
        handleClose(){
          this.$emit("close")
        }
      },
      template: `
            
foo
`
, }); // 根组件 const app = new Vue({ el: "#app", template: `
{{msg}}
`
, data() { return { msg: "hello", showFoo: false, }; }, methods: { handleShowFoo(){ this.showFoo = true; }, handleCloseFoo(){ this.showFoo = false; } } });
script> body>
<body>
    <div id="app">div>
	
    <script>
    // 第一, 在对应的组件 Foo内声明 props对应的value
      Vue.component("Foo", {
        components: {},
        props: ["visible"],
        data() {
          return {};
        },
        model: {   
          // 自定义 
          prop: "visible", // 默认prop -> value
          event: "close",  // 默认event -> input
        },

        methods: {
          handleClose() {
            // this.$emit("close");
            // 第二
             this.$emit("input", false);// 写法一 必须写成input
            this.$emit("close", false);// 写法二 model内定义(自定义名称)
          },
        },
        template: `
            
foo {{visible}}
`
, }); // sync // 根组件 const app = new Vue({ el: "#app", template: `
{{msg}} // // 第三,写上v-model // 此处去掉v-if="showFoo" 可在 子组件内根节点写上 v-if="visible" // 有点局限性,就是别的组件也想通过v-model来用,不行,只能有一个,vue3解决了允许设置多个v-model // vue2想用多个的话可通过sync
`
, // value -> input data() { return { msg: "hello", showFoo: false, }; }, methods: { handleShowFoo() { this.showFoo = true; }, handleClose() { this.showFoo = false; }, }, });
script> body>

六、sync
需求(应用场景):当有多个需求(子组件发出来,父组件去更新的需求)

不再限制props接收的是什么东西 了,随意去定义;
无model;

sync与 v-model对比的优势:sync可以执行多个,支持多个,而v-model只支持一个

<body>
    <div id="app">div>
    <script>
      Vue.component("Foo", {
        components: {},
        props: ["visible", "number"],//第一
        data() {
          return {};
        },
        methods: {
          handleClose() {
            this.$emit("update:visible", false);// 第三,update:,把对应的visible改为false
          },
          handleNumber() {
            this.$emit("update:number", Math.random());
          },
        },
        template: `
            
foo {{visible}}
`
, }); // sync // 根组件 const app = new Vue({ el: "#app", template: `
{{msg}} {{count}} // 第二,:visible.sync
`
, // value -> input data() { return { msg: "hello", showFoo: false, count: 0, }; }, methods: { handleShowFoo() { this.showFoo = true; }, handleClose() { this.showFoo = false; }, }, });
script> body>

七、插槽 slot(一个占用口,到时替换)
插槽slot,用于父组件内容插入子组件(内容分发)

功能:
1.默认插槽,可设置默认值
2.允许写多个插槽
3.具名插槽
4.具名插槽作用域,可定义多个(可传值)
5.缩写 # (v-slot:header 或者 #header)

应用场景:利用slot作用域,做一个模板

<body>
    <div id="app">div>
    <script>
      Vue.component("Foo", {
        components: {},
        data() {
          return {};
        },

        template: `
            
foo
// 4.具名插槽作用域,可定义多个(可传值) ---> count="123" test="234" headers //1.默认插槽,默认值headers
main// 3.具名插槽 name="main" footer
`
, }); // 根组件 const app = new Vue({ el: "#app", template: `
{{msg}} // 通过vue提供的空组件template去指定对应的插槽 // 写法 --> v-slot:header 或者 #header
`
, data() { return { msg: "hello", }; }, });
script> body>

案例:slot-template

<body>
    <div id="app">div>
    <script>
    // TodoList 帮助我们循环数据,遍历出来,可作为一个模板
    // 需求:TodoList 不改变循环,作为模板使用(利用slot做复用,可重复使用)模板的复用
      Vue.component("TodoList", {
        template: `
          
  • {{item.name}}// 利用slot复用
`
, data() { return { items: [{ name: "heihei" }, { name: "haha" }], }; }, }); // Vue.component("TodoList", { // template: ` //
//
    //
  • //

    {{item.name}} + "----heihei"

    //
  • //
//
// `, // data() { // return { // items: [{ name: "heihei" }, { name: "haha" }], // }; // }, // }); // 根组件 const app = new Vue({ el: "#app", template: `

{{item.name}} + "----heihei"

`
, data() { return { msg: "hello", }; }, });
script> body>

复用性
1.mixin -> 解决组件的复用性

<body>
    <div id="app">div>
    <script>
      // mixin -> 组件的复用性 -> 抽离出来公用
      // A组件可用 -> featureA
      // B组件可用 -> featureA
      // function setName(name) {
      //   // 函数的参数
      // }
      
      // 鼠标移动的功能 --> 抽离对象(普通的对象)
      const mousemoveMixin = {
        methods: {
          handleMousemove(e) {
            this.x = e.pageX;
            this.y = e.pageY;
          },
        },
        mounted() {//组件创建完成 写个 事件监听addEventListener
          window.addEventListener("mousemove", this.handleMousemove);
        },
        destroyed() {// 组件销毁完成 写个 移除事件监听removeEventListener
          window.removeEventListener("mousemove", this.handleMousemove);
        },
        data() {
          return {
            x: 0,
            y: 0,
          };
        },
      };
     
      // Foo组件
      Vue.component("Foo", {
        mixins: [mousemoveMixin],
        template: `
            
Foo- {{x}} -- {{y}}
`
, }); // 根组件 const app = new Vue({ el: "#app", mixins: [mousemoveMixin], template: `
`
, });
script> body>

2.slots -> 解决组件的复用性

<body>
    <div id="app">div>
    <script>
    
      // 鼠标移动的功能  Mousemove 对应的组件
      const Mousemove = {
        template: `
          
// 通过slot把x、y传出去
`
, methods: { handleMousemove(e) { this.x = e.pageX; this.y = e.pageY; }, }, mounted() { window.addEventListener("mousemove", this.handleMousemove); }, destroyed() { window.removeEventListener("mousemove", this.handleMousemove); }, data() { return { x: 0, y: 0, }; }, }; // slots // 根组件 // ->问题(多个minxin的话就不清楚x,y是哪个minxin内的了,有命名冲突的问题) vue3 里面解决 -> composition const app = new Vue({ el: "#app", components: {// 注册组件Mousemove Mousemove, }, template: `
// 用注册好的组件 {{x}} - {{y}}
`
, });
script> body>

八、自定义指令
之前的指令如v-if,v-show都是官方的指令,现在可自定义指令

以下生命周期在vue3会改变,和vue2组件生命周期一样的名称
Vue.directive
生命周期(这里的生命周期与vue的生命周期不一样,vue2不一样,vue3对齐生命周期了)
bind
只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
inserted
被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update
所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
componentUpdated
指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind
只调用一次,指令与元素解绑时调用。
案例:自定义v-focus指令

<body>

    <div id="app">div>
    <script>
	//需求:打开input 之后自动聚焦
	
      // 根组件
      //focus
      // input -> input
      // v-if v-show v-focus

      // 聚焦的自定义指令
      Vue.directive("focus", {
        bind(el, binding) {
          // el.focus();不起作用,需要插入父节点后才进行调用
          // console.log(binding)
        },
        inserted(el, binding) {
          console.log(el);
          console.log(binding);
          el.focus();//此处有作用,父节点
        },
      });
      

      const app = new Vue({
        el: "#app",
        template: `
            
`
, data() { return { msg: "hello", }; }, });
script> body>

vue2.0-2——组件化思想、生命周期、props(组件参数)、自定义事件(emit、v-model、sync)插槽(slot,复用性)、自定义指令(复用性)_第3张图片

小作业

  1. (自定义组件)实现组件 PhotoItem 封装展示图片的逻辑
    1. 可通过 props 传入图片路径 imgUrl
    2. 可通过 props 传入图片名称 imgName
解释题目:
	// PhotoItem
   // imgUrl -> 显示一张图片 -> img
   // imgName -> {{imgName}}


   // 

答案:

在这里插入代码片

你可能感兴趣的:(vue2.0,vue.js,前端,javascript)