zoukankan      html  css  js  c++  java
  • vue04-组件化和模块化

    四、组件化

    什么是组件

    借鉴了将一个大的问题拆分成一个个的小问题这种思想 , 就是"基础库"或者“基础组件",意思是把代码重复的部分提炼出一个个组件供给功能使用。

    • 将一个页面拆分成一个个的小组件,可以递归的拆分
    • 每个组件完成自己相关的功能,多个组件共同组成一个页面或者程序
    • 复用性:下次需要同样的功能就可以复用
    • 类似于项目中的一个模块,只不过更加细化了。

    组件的使用

    1. 创建组件的构造器
    2. 注册组件
    3. 使用组件

    组件必须放在vue管理的作用域内,如果是多个标签必须被一个元素包裹,就是有一个唯一的祖先元素

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <script src="../../js/vue.js"></script>
    <div id="app">
        <cpt></cpt>
        <cpt></cpt>
        <cpt></cpt>
        <cpt></cpt>
    </div>
    
    <script>
        // 1. 创建组件构造器
        const component = Vue.extend({
            template: `
                <div>
                    hello
                </div>`,
        });
        // 2. 注册组件 全局组件
        Vue.component('cpt', component);
    
        const app = new Vue({
            el: "#app",
            data: {
                message: "hello world"
            }
        });
    
    </script>
    </body>
    </html>
    

    局部组件

    <div id="app">11
        <cpt></cpt>
        <cpt></cpt>
    </div>
    
    <div id="app2">22
        <cpt></cpt>
    </div>
    <script>
        // 1. 创建组件构造器
        const component = Vue.extend({
            template: `
                <div>
                    hello
                </div>`,
        });
    
        //局部组件 只在app中的作用域有效
        const app = new Vue({
            el: "#app",
            data: {
                message: "hello world"
            },
            components: {
                cpt: component
            }
        });
    
        //未注册组件
        const app2 = new Vue({
            el: "#app2",
            data: {
                message: "hello"
            }
        });
    
    </script>
    

    父子组件

    <div id="app">11
        <pt></pt>
        <pt></pt>
        <pt></pt>
    </div>
    <script>
    
        /*第1个组件构造器*/
        const child = Vue.extend({
            template: `
                <div>
                    child
                </div>`
        });
        // 第二创建组件构造器
        const parent = Vue.extend({
            template: `
                <div>
                    parent
                    <cd></cd>
                </div>`,
            components: {
                cd: child
            }
        });
    
    
        //局部组件 只在app中的作用域有效
        const app = new Vue({
            el: "#app",
            data: {
                message: "hello world"
            },
            components: {
                pt: parent
            }
        });
    
    </script>
    

    组件的传递

    组件不会向上级作用域传递,只会向下传递,孙子没有在爷爷的作用域注册的话孙子只能在父亲的作用域使用

    组件的语法糖

    <div id="app">11
      <pt></pt>
      <pt></pt>
      <pt></pt>
    </div>
    <script>
    
      //局部组件 只在app中的作用域有效
      const app = new Vue({
        el: "#app",
        data: {
          message: "hello world"
        },
        components: {
          pt: {
            //  语法糖直接可以放在注册的地方
            template: `
                <div>
                    hello
                </div>`
          }
        }
      });
    
    </script>
    

    模板的分离

    <script src="../../js/vue.js"></script>
    <div id="app">11
      <pt></pt>
      <pt></pt>
      <pt></pt>
    </div>
    <!--<script type="text/x-template" id="pt">
      <div>
        <div>我是标题</div>
      </div>
    </script>-->
    
    <template id="pt">
      <div>
        <div>我是tempalte</div>
      </div>
    
    </template>
    <script>
    
      //局部组件 只在app中的作用域有效
      const app = new Vue({
        el: "#app",
        data: {
          message: "hello world"
        },
        components: {
          pt: {
            //  语法糖直接可以放在注册的地方
            template: "#pt"
          }
        }
      });
    
    </script>
    

    组件访问数据

    • 组件不能访问实例中的数据
    • 只能访问自己的数据
    • 在子组件中data属性是一个function不是对象,可以返回一个数据对象供它访问
    • 组件也有method属性,它的原型实际上是指向vue的实例的
    <div id="app">11
      <pt></pt>
      <pt></pt>
      <pt></pt>
    </div>
    
    <template id="pt">
      <div>
        <div>我是{{title}}</div>
      </div>
    </template>
    <script>
    
      //局部组件 只在app中的作用域有效
      const app = new Vue({
        el: "#app",
        data: {
          message: "hello world"
        },
        components: {
          pt: {
            
            template: "#pt",
            //是一个函数,且只能访问自己的数据
            data(){
              return {title:"title"};
            }
          }
        }
      });
    
    </script>
    
    
    

    组件的data必须是函数

    • 如果写属性的话,很容易造成多个组件的数据引用指向同一块内存,会相互影响
    • 用function的话你只要每次返回一个匿名对象,他是没有公共引用指向的所以不会影响,如果需要的话你自己可以return 一个公用的引用就会相互影响的
    • 所以为了避免这种bug,data不是function就会报错
    • 必须return 一个对象{}

    父子组件通信

    父传子
    • props属性 : 可以写成数组或者对象,对象可以限制类型,对象更好点,也可以类型写成对象添加更多的限制、给默认值
    • 给默认值的时候如果是对象或者是数组,不能直接用{}、[] 需要用工厂(default(){})来创建
    • 自定义validator
    • 可以自定义一个类作为类型
    <div id="app">
      <pt :msg="msg" :title="title"></pt>
    </div>
    
    <template id="pt">
      <div>
        <div>{{title}}</div>
        <div>{{msg}}</div>
      </div>
    </template>
    <script>
      // 1.注册组件
      const pt = {
        template:"#pt",
        data() {
          return {};
        },
        methods: {},
        // props:["title","msg"] 可以写成数组或者对象,对象可以限制类型,对象更好点
        props:{
          // title:Array,
          title:{
            type: Array,
            default(){
              return [];
            }
          },
          //也可以写成对象的添加更多的限制、给默认值
          msg:{
            type:String,
            default:"",
            required:true,
            //自定义validator 这个待查阅
            validator: function (val) {
              return val == "hello worl";
            }
          }
        }
      }
    
      //局部组件 只在app中的作用域有效
      const app = new Vue({
        el: "#app",
        data: {
          msg: "hello world",
          title:["aaa","bbb","ccc"]
        },
        //字面量简写  pt可替换pt:pt
        components:{pt}
      });
    
    </script>
    
    
    子传父|自定义事件
    • v-on 事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的),所以 v-on:myEvent 将会变成 v-on:myevent——导致 myEvent 不可能被监听到。
    • $emit --》this.$emit('myevent')会传递给父组件的监听事件要同名
    • 推荐你始终使用 kebab-case 的事件名 my-event
    • 子组件尽量和自己的data属性去绑定
    <div id="app">
      <!--  不写参数会默认将$emit事件后传的参数【可多个】传出来,写了参数报错-->
      <pt @child-click="parentClick"></pt>
    </div>
    
    <template id="pt">
      <div>
        <button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button>
      </div>
    </template>
    <script>
      // 1.注册组件
      const pt = {
        template: "#pt",
        data() {
          return {
            categories: [
              {id: "aaa", name: "aaa"},
              {id: "bbb", name: "bbb"},
              {id: "ccc", name: "ccc"},
              {id: "ddd", name: "ddd"}
            ]
          };
        },
        methods: {
          btnClick(ite) {
            // js中这样写不能驼峰,vue可以
            this.$emit('child-click', ite,1);
          }
        }
      };
    
      //局部组件 只在app中的作用域有效
      const app = new Vue({
        el: "#app",
        data: {
          msg: "hello world",
          title: ["aaa", "bbb", "ccc"]
        },
        components: {pt},
        methods: {
          parentClick(obj,a) {
            console.log(obj,a);
          }
        }
      });
    
    </script>
    
    练习
    1. num1、num2从父组件传递过来
    2. 修改num1,dnum1也变,同时传dnum1给父组件,父组件改变num1,也改变了prop1
    3. dnum2一直是dnum1的1%
    <!--1. num1、num2从父组件传递过来
    2. 修改num1,dnum1也变,同时传dnum1给父组件,父组件改变num1,也改变了prop1
    3. dnum2一直是dnum1的1%-->
    <div id="app">
      <pt :cnum1="num1" :cnum2="num2"
          @change1="cc1"
          @change2="cc2"
      ></pt>
    </div>
    
    <template id="pt">
      <div>
        <p>props:{{cnum1}}</p>
        <p>data:{{dnum1}}</p>
        cnum1<input type="text" :value="dnum1" @input="changeProp1"><br>
        <p>props:{{cnum2}}</p>
        <p>data:{{dnum2}}</p>
        cnum2<input type="text" :value="dnum2" @input="changeProp2">
      </div>
    </template>
    <script>
    
      //局部组件 只在app中的作用域有效
      const app = new Vue({
        el: "#app",
        data: {
          num1: 1,
          num2: 2
        },
        methods: {
          cc1(eve1) {
            this.num1 = eve1;
          },
          cc2(eve2) {
            this.num2 = eve2;
          }
        },
        components: {
          pt: {
            template: "#pt",
            props: {
              cnum1: {
                type: Number,
                default: 3
              },
              cnum2: {
                type: Number,
                default: 4
              }
            },
            data() {
              return {
                dnum1: this.cnum1,
                dnum2: this.cnum2,
              };
            }, 
            methods: {
              changeProp1(event1) {
                this.dnum1 = event1.target.value;
                console.log(this.dnum1)
                if (this.dnum1) {
                  this.dnum1 = parseInt(this.dnum1)
                  this.dnum2 = this.dnum1 / 100;
                  this.$emit('change1', this.dnum1);
                } else {
                  this.dnum2 = "";
                }
    
              },
              changeProp2(event2) {
                this.dnum2 = event2.target.value;
                this.$emit('change2', parseInt(this.dnum2));
              }
    
            }
          }
        }
      });
    
    </script>
    
    

    watch

    • watch监听对象不能直接监听,可以用computed代替对象

    • 语法:

      watch:{
      	监听的属性名(newValue, oldValue){
      
      	}
      }
      
    <script src="../../js/vue.js"></script>
    <div id="app">
      {{message}}
      <input type="text" v-model="message">
      {{demo.name}}
      <input type="text" v-model="demo.name">
    </div>
    
    <template id="cd">
      <div>
        aaaaa
      </div>
    
    </template>
    <script>
      const app = new Vue({
        el: "#app",
        data: {
          message: "hello world",
          demo: {
            name: "nameObj"
          }
        },
        computed:{
          demoName(){
            return this.demo.name;
          }
        },
        watch: {
          message(newVal, oldVal) {
            console.log(newVal, oldVal);
          },
          //不能直接监听对象
          // demo(val) {
          //   console.log(val);
          // }
          demoName(val) {
            console.log(val);
          }
        },
        components: {
          cd: {
            template: "#cd"
          }
        }
      });
    
    </script>
    
    
    • 如果是键的路径需要用引号包裹

    • 也可以外部调用

    • immediate和handler

      watch有一个特点,就是当值第一次绑定的时候,不会执行监听函数,只有值发生改变才会执行。如果我们需要在最初绑定值的时候也执行函数,则就需要用到immediate属性。

      比如当父组件向子组件动态传值时,子组件props首次获取到父组件传来的默认值时,也需要执行函数,此时就需要将immediate设为true。

    • **deep: **当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,只有data中的数据才能够监听到变化,此时就需要deep属性对对象进行深度监听

    <div id="app">
      {{demo1.name}}
      <input type="text" v-model="demo1.name">
      {{demo.name}}
      <input type="text" v-model="demo.name">
      <input type="text" v-model="demo2">
    </div>
    
    <script>
      const app = new Vue({
        el: "#app",
        data: {
          message: "hello world",
          demo: {
            name: "nameObj"
          },
          demo1: {
            name: "nameObj"
          },
          demo2:"qweqw"
        },
        computed: {
          demoName() {
            return this.demo.name;
          }
        },
        watch: {
          //如果是键的路径需要用引号包裹
          "demo.name": function (val) {
            console.log(val);
          },
            
          // childrens: {  //监听的属性的名字
          //   handler:function(val){
          //     console.log(val.name);
          //   },
          //   deep: true, //可以监听到一个对象的内部属性变化
           //  immediate: true
          // },
          // "childrens.name":function (val) {
          //   console.log(val);
          // }
        }
      });
      //外部调用
      app.$watch("demo2",function (val) {
        console.log(val)
      })
    </script>
    
    

    访问子组件实例 $children和$refs

    • 一般不会用$children来取子组件
    • $refs.refName | $refs['refName']
      • 如果多个相同的引用会取最后一个
      • 如果绑定的是一个普通标签拿到的就是一个dom对象
    <div id="app">
      <tmp ref="a"></tmp>
      <tmp ref="a"></tmp>
      <tmp ref="b"></tmp>
      <button @click="btnClick">打印子组件</button>
    </div>
    <template id="tmp">
      <div>
        <p>哈哈哈</p>
      </div>
    </template>
    
    <script>
      const app = new Vue({
        el: "#app",
        data: {
          message: "hello world"
        },
        methods:{
          btnClick(){
            //1. 一般不会用$children来取子组件
            // console.log("第一个子组件:",this.$children[0]);
            // console.log("所有子组件:",this.$children);
    
            // 2.$refs.refName|['refName']
            console.log("所有组件有ref属性的组件:",this.$refs);
            //如果多个相同的引用会取最后一个
            console.log("取得固定的ref的元素:",this.$refs["a"]);
            console.log("取得固定的ref的元素:",this.$refs.b);
          }
        },
        components: {
          tmp: {
            template: "#tmp"
          }
        },
    
      });
    
    </script>
    

    访问父组件实例

    • 不建议使用this.$parent,会让组件的耦合增强不够独立
    • 祖先组件this.$root
    <div id="app">
      <tmp></tmp>
    </div>
    <template id="tmp">
      <div>
        <p>哈哈哈</p>
        <button @click="btnClick">打印父组件</button>
      </div>
    </template>
    
    <script>
      const app = new Vue({
        el: "#app",
        data: {
          message: "hello world"
        },
        components: {
          tmp: {
            template: "#tmp",
            methods: {
              btnClick() {
                //1. 不建议使用,会让组件的耦合增强不够独立
                console.log("打印直系父组件:", this.$parent);
                //祖先组件
                console.log("打印root组件:", this.$root);
              }
            }
          },
        },
    
      });
    
    

    插槽slot

    • 拓展组件像回调函数一样,usb接口一样
    • 插槽的基本使用
    • 插槽的默认值 默认值
    <!--1. 插槽的基本使用 <slot></slot>-->
    <!--2. 插槽的默认值 <slot>默认值</slot>-->
    <div id="app">
      <tmp></tmp><br>
      <tmp></tmp><br>
      <tmp></tmp><br>
      <tmp><div>我是插槽</div></tmp>
      <tmp><i>我是插槽i</i></tmp>
    </div>
    <template id="tmp">
      <div>
        <p>哈哈哈</p>
        <slot><p>我是默认值*******</p></slot>
        <p>娃娃</p>
      </div>
    </template>
    
    <script>
      const app = new Vue({
        el: "#app",
        data: {
          message: "hello world"
        },
        components: {
          tmp: {
            template: "#tmp"
          },
        }
    
      });
    
    </script>
    
    具名插槽
    <div id="app">
      <tmp ><a slot="right" href="#">我替换右边</a></tmp><br>
      <tmp ><a slot="left" href="#">我替换左边</a></tmp><br>
      <tmp><a href="#">我替换没名字的</a></tmp><br>
    </div>
    <template id="tmp">
      <div>
        <slot name="left"><p>我是默认值left</p></slot>
        <slot name="center"><p>我是默认值center</p></slot>
        <slot name="right"><p>我是默认值right</p></slot>
        <slot><p>我是默认值没有名字</p></slot>
      </div>
    </template>
    
    <script>
      const app = new Vue({
        el: "#app",
        data: {
          message: "hello world"
        },
        components: {
          tmp: {
            template: "#tmp"
          },
        }
    
      });
    

    编译作用域

    • 始终使用自己组件中的变量
    <div id="app">
    <!--    在谁的作用域用谁的变量-->
      <cp v-show="isShow"></cp>
    </div>
    <template id="cp">
    
      <div v-show="isShow"><!-- div父元素初始化的时候不受影响 -->
        <a href="">aaa</a>
        <button v-show="isShow">按钮</button>
      </div>
    </template>
    
    <script>
      const app = new Vue({
        el: "#app",
        data: {
          message: "hello world",
          isShow: true
        },
        components: {
          cp: {
            template: "#cp",
            data() {
              return {
                isShow: false
              };
            }
          }
        }
      });
    
    </script>
    
    作用域插槽
    • 父组件想要替换子组件的插槽的数据,数据的具体值还是由子组件来决定
    • slot-scope="slotData",类似于该组件的对象,2.5之前要用template标签
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
    </head>
    <body>
    <script src="../../js/vue.js"></script>
    <div id="app">
      <cp>
        <!--    slotData:类似于该组件的对象,2.5之前要用template-->
        <template slot-scope="slotData">
          <!--      取得绑定在组件中的数据-->
          <span v-for="item in slotData.datas">{{item}}-</span>
        </template>
      </cp>
    
      <cp>
        <template slot-scope="slotData">
          <!--      join方法将数组拼接成字符串-->
          <span>{{slotData.datas.join(' * ')}}</span>
        </template>
      </cp>
    </div>
    <template id="cp">
    
      <div>
        <!--    作为传递的数据-->
        <slot :datas="languages">
          <ul>
            <li v-for="item in languages">{{item}}</li>
          </ul>
        </slot>
      </div>
    </template>
    
    <script>
      const app = new Vue({
        el: "#app",
        data: {
          message: "hello world",
        },
        components: {
          cp: {
            template: "#cp",
            data() {
              return {
                languages: ['java', 'javascript', 'css', 'html', 'vb', 'python']
              };
            }
          }
        }
      });
    
    </script>
    </body>
    </html>
    

    五、es6模块化

    把功能进行划分,将同一类型的代码整合在一起,所以模块的功能相对复杂,但都同属于一个业务

    为什么有模块化

    • js是按顺序加载的,所以一般相互依的js是具有强制性的
    • 多个js文件定义的引用会污染全局变量,多人协作开发可能会有冲突
    • 可以用闭包

    闭包解决多人协作开发

    • 只需要写好自己的模块化的命名,就可以很好的避免冲突了,相当于把所有的容错点都聚焦在一个点上,犯错的机会就少了,
    • 但是代码的复用性还是很差
    // ;是为了防止其他的导入js相互影响
    ;var xm01 = (function xiaoming01() {
      return {
        aa:"asdas",
        flag: true
      };
    }())
    
    
    //js文件2
    ;(function () {
      if (xm01.flag) {
        alert("xm01.flag:" + xm01.flag);
      }
    }());
    

    组件化类似模块化的更细粒度,组件充当了基本类库一样的东西目的是复用拓展性,模块主要是以功能区分类别划分尽量隔离其他业务

  • 相关阅读:
    Java多线程总结
    Linux命令总结
    Java笔记
    JDK7和JDK8一些重要新特性
    第八周(11.04-11.10)----每周报告
    第八周(11.04-11.10)----结对项目----逆波兰
    第八周(11.04-11.10)----个人作业----历年学生作品点评
    第七周PSP(10.27-11.03)
    第七周(10.27-11.03)----补交第六周(10.20-26)每周例行报告
    个人项目----词频统计WEB(部分功能)
  • 原文地址:https://www.cnblogs.com/zpyu521/p/12309560.html
Copyright © 2011-2022 走看看