Vue.js 2.0 Study Notes (3) Component Development

Vue.js 2.0 Study Notes (3) Component Development

Article directory

1. Component development

Component-based development: According to the idea of ​​encapsulation , the reusable UI on the page is deconstructed and encapsulated into components to facilitate project development and maintenance

The idea of ​​component development: split complex problems into many small problems. If we put all the logical processing in the page together, the processing will be very complicated, and it is not conducive to subsequent management and expansion. If we split a page into small functional blocks, and each functional block completes its own independent functions , then the management and maintenance of the entire page will become easier

1.1 Three components in components in vue

Each .vue component consists of 3 parts, namely:

  • template : the template structure of the component , note: there can only be one root node in the template
  • script : JavaScript behavior of the component
  • style : the style of the component

Note: The data node in the script in the .vue component cannot point to an object, and the data in the component must be a function

 

1.2 this in the methods method in the component

Key points: In a component, this is the instance object of the current component

 

1.3 Basic steps for using components

Three steps for the use of components: (Vue 2.x does not actually use it, it is just the basis)

  • Create a component constructor : call Vue.extend()
  • Register component : call Vue.component()
  • Using components : used within the scope of the Vue instance

 

(1)Vue.extend()

  • Calling Vue.extend() creates a component constructor
  • Usually when creating a constructor, the template passed in represents the template of our custom component, which is the HTML code to be displayed where the component is used

 

(2)View.component()

  • Register the component constructor just now as a component, and give him a component label name
  • Passed parameters: the tag name and component constructor of the registered component

 

(3) Using components

The component must be mounted under a certain Vue instance, otherwise it will not take effect

Three steps to use components today: (now mainstream use)

  1. Import components: use import to import the required components

    import Login from '@/components/Login.vue'
    
  2. Register components: use the components node to register components

    export default{
          
          
        components:{
          
          
            //右边的Login 对应import 中的组件名,左边的字符串对应使用便签时的标签名
            //'Login': Login 
            Login  //当key 和 value的值相同时,可以简写
        }
    }
    
  3. Using components: used in the scope of the Vue instance in the form of tags

    <Login></Login>
    

       

1.4 Global components and local components

// 注册组件(全局组件,意味着可以在多个Vue实例下使用)
import cpnC from '@/components/cpnC.vue'
// 参数1:字符串格式,表示组件的“注册名称”
// 参数2:需要被全局注册的那个组件
Vue.component('cpn',cpnC); //全局组件

const app = new Vue({
    
    
    el:'#app',
    data: {
    
    
        message:'你好'
    },
    components: {
    
    
        //cpn使用组件时的标签名
        cpn: cpnC //局部组件
    }
})

       

1.5 Parent and Child Components

// 创建第一个组件构造器(子组件)
const cpnC1 = Vue.extend({
    
    
    template:`
        <div>
            <h2>xxxx</h2>
            <p>hhhh</p>
        </div>
	`
});

// 创建第二个组件构造器(父组件)
const cpnC2 = Vue.extend({
    
    
    template:`
    <div>
        <h2>xxxx</h2>
        <p>hhhh</p>
        <cpn1></cpn1>
    </div>
    `,
    components: {
    
    
        cpn1: cpnC1
    }
});

// root组件
const app = new Vue({
    
    
    el:"#app",
    data: {
    
    
        message: '你好啊'
    },
    components: {
    
    
        cpn2: cpnC2
    }
})

       

1.6 Syntactic sugar (important, the previous way of writing is no longer used)

1.6.1 Register global components

Vue.component('cpn1',{
    
    
    template:`
        <div>
            <h2>xxxx</h2>
            <p>hhhh</p>
        </div>
	`
});

Note: cpn1 is wrapped in single or double quotes

       

1.6.2 Register local components

const app = new Vue({
    
    
    el:"#app",
    data: {
    
    
        message: '你好啊'
    },
    components: {
    
    
       'cpn2': {
    
    
           template:`
              <div>
                 <h2>xxxx</h2>
                 <p>hhhh</p>
              </div>
           `
       } 
    }
})

Note: cpn2 is wrapped in single or double quotes

       

1.7 Separate writing of component templates

<!-- 1.script标签,注意类型必须是text/x-template -->
<script type="text/x-template" id="cpn">
    <div>
      <h2>xxxx</h2>
      <p>hhhh</p>
    </div>
</script>

// 注册全局组件
<script>
    Vue.component('cpn', {
      
      
      template: '#cpn'
    })
</script>

       

1.8 Components access vue data

  • A component is an encapsulation of a separate functional module. This module has its own HTML and should also have its own data attributes.
  • Components cannot directly access the data in the Vue instance

1.8.1 Storage of component's own data

  • The component object also has a data attribute (it can also have attributes such as methods), but the data attribute must be a function, that is, data() . The reason: functions return their own objects, and they will not affect each other.
  • This function returns an object, which holds the data inside
<div id="app">
    <my-cpn></my-cpn>
</div>

<template id="myCpn">
    <div>消息:{
   
   {message}}</div>
</template>

<script>
    const app = new Vue({
      
      
        el:"#app",
        components: {
      
      
            'my-cpn': {
      
      
                template: "#myCpn",
                data() {
      
      
                    return {
      
      
                        message: 'hello world'
                    }
                }
            }

        }
    })
</script>

       

1.9 Style conflicts between components

       By default, uninstalling the styles in the .vue component will take effect globally, so it is easy to cause style conflicts between multiple components, leading to the root cause of style conflicts:

  1. In a single-page application, the DOM structure of all components is presented based on the unique index.html page
  2. The styles in each component will affect the DOM elements in the entire index.html page

Solution: Use the scoped attribute in the style node

       

1.9.1 The underlying principle of scoped

       When the scoped attribute is added to the style node of .vue, the style is localized and only takes effect in the current .vue file , without conflicts. The underlying principle is that after using scoped, vue is each html tag in the file Both add custom attributes of data-v to use attribute selectors in styles to resolve style conflicts

1.9.2 Use deep to modify the style of the self-component

Role : Ability to change the style of the child component in the parent component

Application scenario : When using a third-party component library (such as: vant), if there is a need to modify the default style of the third-party component library, you need to use /deep/

/deep/ h5 {
    
    
    color: pink;
}

1.10 instance object of vue component

       The browser does not recognize the .vue file, and needs to rely on the vue-template-compiler in packa.json -> devDependencies to compile and parse it into a js file and pass it to the browser for execution

 

2. Data sharing between components

2.1 Communication between parent and child components

       Background: We know that component instances are scoped in isolation. This means that parent component data or Vue instance data cannot (and should not) be directly referenced within the child component's template.
       However, in development, there are often some data that need to be passed from the upper layer to the lower layer. For example: in the page, a lot of data is requested from the server, and part of the data is not displayed by the entire large component, but requires the following subcomponents to show. At this time, the child component will not send a network request again, but directly let the large component (parent component) pass the data to the small component (child component)

       

2.1.1 Method of parent-child component communication

  • Parent components pass data to child components through props
  • Child components send information to parent components through events
  • Summary: props down, events up

       

2.1.2 Basic usage of props (parent component transmits information to child component)

       props is a custom attribute of a component . When encapsulating a common component, using props properly can greatly improve the reusability of the component . The syntax format is as follows:

export default {
    
    
    //组件自定义属性
    props: ['自定义属性a','自定义属性b'...],
    //组件的私有属性
    data() {
    
    
        return{
    
     }
    }
}

There are two ways to pass data from parent components to child components: dynamic and static

1. Static props

       The child component must explicitly declare the data it expects with propsoptions , and static props achieve the purpose of passing values ​​by adding characteristics to the placeholder of the child component in the parent component

<div id="app">
    <child-cpn :message="message"></child-cpn>
</div>

<template id="childCpn">
    <div>消息:{
   
   {message}}</div>
</template>

<script>
    const app = new Vue({
      
      
        el:"#app",
        data: {
      
      
            message: 'hello'
        },
        components: {
      
      
            'child-cpn': {
      
      
                template: "#childCpn",
                props: ['message']
            }

        }
    })
</script>

The process of props passing:

  1. Initialize data in Vue instance
  2. Initialize props in subcomponents
  3. <child-cpn :message="message"></child-cpn> Pass the data in data to props by: message="message" (the message in double quotes is the bound data variable)
  4. Display the data in props in subcomponents
image-20211030170936837

       

2. Dynamic props

       In the template, to dynamically bind the data of the parent component to the props of the child template, similar to binding to any normal HTML feature, is to use v-bind. Whenever the data of the parent component changes, the change is also transmitted to the child component

       

3. Two expressions of props value

  • String array : the string in the array is the name when passing, and the default value cannot be set
const cpn = {
    
    
    template: '#cpn',
    props:['cmovies','cmessage']
}
  • Object : The object can set the type when passing, or set the default value , etc.
const cpn = {
    
    
    template: '#cpn',
    props: {
    
    
        // 1.类型限制
        // cmovies: Array,
        // cmessage: String
        
        //2.或提供一些默认值
        cmessage: {
    
    
            type: String,
            default: 'hhhhh',
            required: true  //意味着必须传值,否则报错
        }
        // 类型是对象或数组时,默认值必须是一个函数
        cmovies: {
    
    
        	type: Array,
            default() {
    
    
				return []
            }
    	}
    }
}

Main points:

  1. The value of props is read-only , do not directly modify the value in props, an error will be reported
  2. The data in props can be used directly in the template structure

       

4. Naming conventions in props

  • For the attributes declared by props, in the parent HTML template, the attribute name needs to be written with a dash, and humpcase is not supported
var parentNode = {
    
    
  template: `
  <div class="parent">
    <child my-message="aaa"></child>
    <child :my-message="message"></child>
  </div>`,
  components: {
    
    
    'child': childNode
  }
};
  • When the sub-level props attribute is declared, it can be written in camelcase or underline ; when the child-level template uses variables passed from the parent , it needs to use the corresponding camelcase
var childNode = {
    
    
  template: '<div>{
    
    {myMessage}}</div>',  //子级模板使用从父级传来的变量:使用小驼峰写法
  props:['myMessage']            // 子级props属性声明时,使用小驼峰或者中划线写法都可以
  // 或者 props:['my-message']
}

       

5. props validation

When verifying the type of props, etc., you need to use object writing

Validation supported data types are:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

When there is a custom constructor, custom types are also supported

Vue.component('my-component', {
    
    
    props: {
    
    
        //基础的类型检查(‘null’匹配任何类型)
        propA: Number,
        // 多个可能的类型
        propB: [String, Number],
        // 必填的字符串
        propC: {
    
    
            type: String,
            required: true
        },
        // 带有默认值的数字
        propD: {
    
    
            type: Number,
            default: 100
        },
        // 带有默认值的对象
        propE: {
    
    
            type: Object,
            // 对象或数组默认值必须从一个工厂函数获取
            default: function() {
    
    
                return {
    
     message: 'hello'}
            }
        },
        // 自定义验证函数
        propF: {
    
    
            validator: function(value) {
    
    
                return ['success','warning','danger'].indexOf(value) !== -1;
            } 
        }
    }
})

       

2.1.3 Child and father

Method: complete through custom events

The flow of custom events:

  • In the child component , the event is triggered by $emit()
  • In the parent component , listen to child component events through v-on (@)
<!-- 父组件模板 -->
<div id="app">
    <cpn @itemClick="cpnClick"></cpn> // 监听到子组件事件:itemClick后触发cpnClick事件
</div>

<!-- 子组件模板 -->
<template id="childCpn">
    <div>
        <button v-for="item in cactegories"
                @click="btnClick(item)">
            {
   
   {item.name}}
        </button>
    </div>
</template>

<script>
    // 子组件
    const cpn = {
      
      
        template: '#cpn',
        data() {
      
      
            return {
      
      
                categories:[
                    {
      
      id: 'aaa', name: '1'},
                    {
      
      id: 'bbb', name: '2'},
                    {
      
      id: 'ccc', name: '3'},
                    {
      
      id: 'ddd', name: '4'}
                ]
            }
        },
        methods: {
      
        // 子组件的自定义事件
            btnClick(item) {
      
      
                // 发射事件
                this.$emit('item-click',item)
            }
        }
    }

    // 父组件
    const app = new Vue({
      
      
        el:"#app",
        data: {
      
      
            message: 'hello'
        },
        components: {
      
      
            cpn
        },
        methods: {
      
       //父组件接收事件
            cpnClick(item) {
      
      
                console.log('cpnClick');
            }
        }
    })
</script>

Case: Implement two buttons +1 and -1, and modify the counter after clicking. The operation process is completed in the child component, and the display is handed over to the parent component, so the counter in the child component needs to be passed to a property of the parent component, such as total

<div id="app">
    <cpn :number1="num1"
         :number2="num2"
         @num1change="num1change"
         @num2change="num2change">
    </cpn>
</div>

<!-- 子组件模板 -->
<template id="cpn">
    <div>
        <h2>props:{
   
   {number1}}</h2>
        <h2>data:{
   
   {dnumber1}}</h2>
        <input type="text" v-model="dnumber1">
        <h2>props:{
   
   {number2}}</h2>
        <h2>data:{
   
   {dnumber2}}</h2>
        <input type="text" v-model="dnumber2">
    </div>
</template>

<script>
    // 子组件
    const cpn = {
      
      
        template: '#cpn',
        data() {
      
      
            return {
      
      
               dnumber1:this.number1,
               dnumber2:this.number2
            }
        },
        methods: {
      
        // 子组件的自定义事件
            num1Input(event) {
      
      
                //将input中的value赋值到dnumber中
                this.dnumber1 = event.target.value;
                this.$emit('num1change',this.dnumber1)
                this.dnumber2 = this.dnumber1 *100;
                this.$emit('num2change',this.dnumber2)
            }
            num2Input(event) {
      
      
                //将input中的value赋值到dnumber中
                this.dnumber2 = event.target.value;
                this.$emit('num2change',this.dnumber2)
                this.dnumber2 = this.dnumber2 /100;
                this.$emit('num1change',this.dnumber1)
            }
        }
    }
	// 或者用watch监听属性的变化
     const cpn = {
      
      
        template: '#cpn',
        data() {
      
      
            return {
      
      
               dnumber1:this.number1,
               dnumber2:this.number2
            }
        },
        watch: {
      
      
            dnumber1(newvalue) {
      
      
                this.dnumber2 = newvalue * 100;
                this.$emit('num1change',newvalue);
            },
            dnumber2(newvalue) {
      
      
                this.dnumber1 = newvalue / 100;
                this.$emit('num2change',newvalue);
            }
        }
    }
    
    // 父组件
    const app = new Vue({
      
      
        el:"#app",
        data: {
      
      
            num1:1,
            num2:0
        },
        components: {
      
      
            cpn: {
      
      
                template: '#cpn',
                props: {
      
      
                    number1: Number,
                    number2: Number
                }
            }
        },
        methods: {
      
      
            num1change(value) {
      
      
                this.num1 = parseFloat(value)
            },
            num2change(value) {
      
      
                this.num2 = parseFloat(value)
            }
        }
    })
</script>

Note: Do not directly bind num1, num2 to change the value, write a value when changing the data

       

2.2 Access method of parent-child components

  • Parent component accesses child components : use $childrenor$refs
  • Child components access parent components : use $parent(available when accessing root components $root)

       

2.2.1 Parent component access child component

this.$children is an array type that contains all child component objects

2.2.2 Child components access parent components

<template id="cpn">
    <div>
        <h2>hhh</h2>
        <button @click="btnClick"></button>
    </div>
</template>

<script>
    const app = new Vue({
      
      
		el:"#app",
        data:{
      
      
            message:'hhh'
        }
        components: {
      
      
            cpn: {
      
      
                template: '#cpn',
                data() {
      
      
                    return {
      
      
                        name:'我是cpn组件的name'
                    }
                },
                components: {
      
      
                    ccpn: {
      
      
                   		template: "#ccpn",
                        methods: {
      
      
                            btnClick() {
      
      
                                //1. 访问父组件$parent
                                console.log(this.$parent);
                                console.log(this.$parent.name);
                                
                                //2. 访问根组件$root
                                console.log(this.$root);
                                console.log(this.$root.message);
                            }
                        }
                    }
           		}
            }
        }
    })
</script>

       

2.3 Communication between sibling components

In vue 2.x , the data sharing scheme between sibling components is: eventBus

eventBus usage steps:

  1. Create eventBus.js module and share a Vue instance object externally

  2. On the data sender , call the bus.$emit('event name', the data to be sent) method to trigger a custom event

  3. On the data receiver , call the bus.$on('event name', event handler) method to register a custom event

       

3. Component life cycle and life cycle functions

3.1 The concept of life cycle and life cycle function

You can refer to the life cycle in Note (2)

  • Life cycle : refers to the entire stage of a component from creation -> operation -> destruction , emphasizing a time period
  • Life cycle function : It is a built-in function provided by the vue framework , which will be automatically executed in order along with the life cycle of the component , emphasizing the time point

       

Four, ref reference

Background: In vue, it is a data-driven view that does not need to operate the DOM. What should I do if I need to manipulate the DOM in a vue project and need to get a reference to a DOM element on the page?

4.1 What is ref reference

       ref is used to assist developers to obtain references to DOM elements or components without relying on jQuery

       Each vue component instance contains a refs ( refs (r e f s ( starting with vue's built-in object) object, which storesthe reference of the corresponding DOM element or component. By default,$refs of the component points to an empty object

<template>
  <div>
    <h1 ref="myh1">App 根组件</h1>
    <button @click="showThis"></button>
  </div>
</template>

<script>
export default {
      
      
  methods: {
      
      
    showThis() {
      
      
      this.$refs.myh1.style.color = 'red';
    }
  }
}
</script>

       

4.2 Use ref to refer to components

<template>
  <div>
    <h1 ref="myh1">App 根组件</h1>
    <button @click="showThis">打印this</button>
    <button @click="onReset">重置 Left 组件中的 count 值为 0</button>

    <div class="box">
      <!-- 渲染 Left 子组件 -->
      <Left ref="comLeft"></Left>
    </div>
  </div>
</template>

<script>
import Left from '@/components/Left.vue'
export default {
      
      
  methods: {
      
      
    showThis() {
      
      
      this.$refs.myh1.style.color = 'red';
    },
    onReset() {
      
      
      // resetCount() 和 count 分别是 Left 组件的方法和数据
      this.$refs.comLeft.resetCount();
      // this.$refs.comLeft.count = 0; (或者)
    }
  },
  components: {
      
      
    Left
  }
}
</script>

       

4.3 this.$nextTick

Function: Delay the code until the DOM element is re-rendered before executing

Let's look at a case first, the requirements:

  • Realize the mutually exclusive switching between the input box and the display button, the button is not displayed when there is an input box, and the input box is not displayed when there is a button
  • Automatically focus when displaying the input box
  • When the input box loses focus, it automatically hides and displays the button
<template>
  <div>
    <input type="text" v-if="inputVisible" @blur="showButton" ref="iptRef">
    <button v-else @click="showInput">展示输入框</button>
  </div>
</template>

<script>
export default {
      
      
  data() {
      
      
    return {
      
      
      inputVisible: false
    }
  },
  methods: {
      
      
    //点击按钮,展示输入框
    showInput() {
      
      
      this.inputVisible = true; 
      //让展示出来的文本框,自动获得焦点
      //此时只是数据更新了,结构还未渲染,所以页面上呈现的还是按钮,也就拿不到输入框的引用
      // this.$refs.iptRef.focus(); //无效的,会报错,this.$refs.iptRef为undefined
    },
    showButton() {
      
      
      this.inputVisible = false;
    }
  }
}
</script>

       this.$refs.iptRef.focus(); did not take effect, and an error was reported. Reason: At this time, only the data is updated, and the structure has not been rendered, so the button is still displayed on the page, and the reference of the input box cannot be obtained

       Solution to the above problems: use this.$nextTick(callback) method

<script>
export default {
      
      
  data() {
      
      
    return {
      
      
      inputVisible: false
    }
  },
  methods: {
      
      
    //点击按钮,展示输入框
    showInput() {
      
      
      this.inputVisible = true; 
      // 将代码延迟到 DOM 元素重新渲染完毕之后再执行
      this.$nextTick(() => {
      
      
          this.$ref.iptRef.focus();
      })
    },
    showButton() {
      
      
      this.inputVisible = false;
    }
  }
}
</script>

       Note: The updated() method cannot be used for the above problem, the reason: click the button once, inputVisible becomes true to display the input box, the data changes, trigger updated() to get the focus, click again, the hidden input box displays the button, but at this time The data has also changed, triggering updated() to get the focus, but at this time there is no input box, so an error will be reported

Guess you like

Origin blog.csdn.net/weixin_45950819/article/details/120916822