In this chapter, interviewers will often ask the following questions. Look at this article with these questions in mind. After studying, you can think about your answer again.

  1. What are the component categories? How do you deal with business components in real projects? What if there are reusable components?
  2. Component communication Besides parent-child component communication, what other modes do you know?
  3. Why can’t child components modify data in parent components?
  4. Why does a component’s data have to be a function?
  5. Describe the Vue lifecycle, which ones do you use? Describe their application scenarios respectively

Component based

What is a component

If you have used bootstrap before, you will be familiar with this term. In fact, we have been exposed to this term since a very early time

Typically an application blocks this in the form of a nested component tree:

Local components

Doggerel using local components: build a child to hang a child to use

Note: In the component this data must be a function that returns an object

<div id="app">
      <! -- 3. Use child components -->
    <App></App>
</div>
<script>
//1. Create child components
const App = {
    // must be a function
    data() {
        return {
            msg: 'I'm an App component'}},components: {
        Vcontent
    },
    template: ` 
       
`
} new Vue({ el: '#app'.data: {},components: { // 2. Mount child components App } })
</script> Copy the code

Global components

Create a global component with Vue.component(component name,{}) that can be used in any template

Vue.component('Child', {template:'
      

I am a child component

'
}) Copy the code

Component communication

The father the son

If a web page has a blog component, it won’t work if you can’t pass it data like the title and content of a particular blog post. That’s where Prop comes in

Parent component to child component: pass data to child component via Prop

Vue.component('Child', {template:` < div > < h3 > I was a child component < / h3 > < h4 > {{childData}} < / h4 > < / div > `.props: ['childData']})const App = {
    data() {
        return {
            msg: 'I'm the value passed in by the parent component'}},template: ` 
      
`
.computed: {}}Copy the code
  1. Declare in the child component that props receives properties mounted on the parent component
  2. Can be used arbitrarily in the template of the child component
  3. Bind custom properties in the parent component
Child the parent

There are some features on the web page that may require us to communicate with the parent component

Child component to parent component communication: listen for child component events, using the event to throw a value

Vue.component('Child', {
    template: ` < div > < h3 > I was a child component < / h3 > < h4 > {{childData}} < / h4 > < input type = "text" @ input = 'handleInput "/ > < / div > `.props: ['childData'].methods: {handleInput(e){
            const val = e.target.value;
            // Use $emit to trigger events for child components
            this.$emit('inputHandler',val); }}})const App = {
    data() {
        return {
            msg: 'I'm the value passed in by the parent component'.newVal:' '}},methods: {input(newVal){
            // console.log(newVal);
            this.newVal = newVal; }},template: ` < div > < div class = 'father' > data: {{newVal}} < / div > <! --> <Child :childData = 'MSG' @inputhandler = 'input'></Child> </div>.computed: {}}Copy the code
  1. Bind custom events to children within a parent component

  2. Emit native events in the child component. Emit custom events in the event function via this.$emit

The parallel component

In development, there might be unrelated component communication, such as a blog content display component, and a form submission component, and now we submit data to the blog content display component, which is a little bit cumbersome to display.

To solve this problem, we can use bus in VUE to create a central event bus

const bus = new Vue();
// Central event bus
Vue.component('B', {
    data() {
        return {
            count: 0}},template: `
<div>{{count}}</div>
`.created(){
        // $on bind event
        bus.$on('add'.(n) = >{
            this.count+=n;
        })
    }
})

Vue.component('A', {
    data() {
        return{}},template: '
      
< button@click ='handleClick'> Add to cart
.methods: {handleClick(){ // $emit triggers the event bus.$emit('add'.1); }}})Copy the code
Communication modes of other components

Parent components provide variables, and child components inject variables through inject. No matter how deeply the components are nested

Vue.component('B', {
    data() {
        return {
            count: 0}},inject: ['msg'].created(){
        console.log(this.msg);

    },
    template: ` 
      
{{msg}}
`
, }) Vue.component('A', { data() { return{}},created(){ // console.log(this.$parent.$parent); // console.log(this.$children); console.log(this); }, template: `
`
}) new Vue({ el: '#app'.data: {},components: { // 2. Mount child components App } }) Copy the code

slot

Anonymous slot

The child component defines the slot slot, but it is not named, so it can also be said to be the default slot. Anything inserted into the parent element is inserted into the slot by default

Vue.component('MBtn', {
    template: `  `.props: {
        type: {
            type: String.defaultValue: 'default'}}})const App = {
    data() {
        return{}},template: ` < div > < m - BTN > login < / m - BTN > < m - BTN > register < / m - BTN > < m - BTN > submit < / m - BTN > < / div > `,}new Vue({
    el: '#app'.data: {},components: {
        // 2. Mount child components
        App
    }

})
Copy the code
A named slot

Named slots can appear in different places and have no limit on the number of times they can appear. As long as the name matches then the content will be inserted into the slot of the name

Vue.component('MBtn', {template:`  `.props: {type: {type: String.defaultValue: 'default'}},methods: {clickHandle(){
            this.$emit('click'); }}})const App = {
    data() {
        return{}},methods: {handleClick(){
            alert(1);
        },
        handleClick2(){
            alert(2); }},template: '
      
'
,}new Vue({ el: '#app'.data: {},components: { App } }) Copy the code
Scope slot

Normally a normal slot is something that is passed in by the parent to determine the contents of the slot. But sometimes we need to get some data from a child component, and the scope slot comes in handy

Vue.component('MyComp', {
    data(){
        return {
            data: {username:'Little Pony'}}},template: ` 
      
`
}) const App = { data() { return{}},template: ` <div> <MyComp> <! --> <template v-slot:default='user'> {{user.data.username}} </template> </MyComp> <MyComp> <! <template v-slot:one='user'> {{user.data.username}} </template> </MyComp> </div> ',}new Vue({ el: '#app'.data: {},components: { App } }) Copy the code
Scope slot application

Let’s start with our hypothetical application common scenario. We have developed a to-do list component that is used by many modules and now require a checkbox effect to be added to completed to-do items without affecting the functionality and presentation of the modules that have been tested.

In other words, the to-do list component should satisfy the following points

  1. Previous data format and reference interface unchanged, normal display
  2. New function modules add check marks
const todoList = {
   data(){
       return{}},props: {todos:Array.defaultValue:[]
   },
   template:` 
      
  • {{item.title}}
`
} const App = { data() { return { todoList: [{title: 'How are you, brother?'.isComplate:true.id: 1 }, { title: 'I'm all right, buddy.'.isComplate:false.id: 2 }, { title: 'What are you doing?'.isComplate:false.id: 3 }, { title: 'Smoking, drinking, perming'.isComplate:true.id: 4}}},components:{ todoList }, template: ` `,}new Vue({ el: '#app'.data: {},components: { App } }) Copy the code

The life cycle

“You don’t have to understand everything right away, but it becomes more and more valuable as you learn and use it.

When you’re working on a project and you run into this kind of problem, go back to this diagram

What is the life cycle

Each Vue instance goes through a series of initialization procedures when it is created. For example, create data from the start, initialize data, compile templates, mount Dom, update Dom as data changes, unload, and so on. We call this sequence of processes the Vue lifecycle. Vue instance is the process from creation to destruction. There are also some functions called lifecycle hooks that run along the way, which give users the opportunity to add their own code at different stages, using each hook to complete our business code.

To work with

Lifecycle hook

beforCreate

Hook events that are executed after the instance is initialized but before the instance is created

Vue.component('Test', {data(){
       return {
           msg:'Little Pony'}},template:` 
      

{{msg}}

`
.beforeCreate:function(){ // Before component creation console.log(this.$data);//undefined}})Copy the code

Effect:

Data observation and event configuration are not ready before the instance is created. There is no data and no DOM generation

created

The hook that executes after the instance is created

created() {
    console.log('Component Creation'.this.$data);
}
Copy the code

Effect:

After the instance is created, we can read the value of the data, but the DOM is not yet generated, so we can initiate Ajax at this point

beforeMount

The hook that fires when the compiled HTML is mounted into the corresponding virtual DOM and the page has no content. That is, this stage is interpreted as: About to mount

beforeMount(){
    // is called before mounting data to the DOM
    console.log('Before DOM mount'.document.getElementById('app'));
}
Copy the code

Effect:

mounted

The event hook function that executes after the compiled HTML is mounted to the page

mounted() {
    console.log('DOM mount complete '.document.getElementById('app'));
}
Copy the code

Effect:

BeforeUpdate and updated
beforeUpdate() {
    // Call this hook before updating the DOM. Apply: the original DOM can be retrieved
    console.log('Before DOM update'.document.getElementById('app').innerHTML);
},
updated() {
    // Call this hook after updating the DOM to get the latest DOM
  console.log('DOM update completed '.document.getElementById('app').innerHTML);
}
Copy the code

Effect:

BeforeDestroy and destroyed

When the subcomponent switches in the V-IF condition, the group valence is in the create and destroy state

beforeDestroy() {
    console.log('beforeDestroy');
},
destroyed() {
    console.log('destroyed');
},
Copy the code
Activated and deactivated

The following method is called when used in conjunction with vue’s built-in

component

The

component is used to cache the current component

activated() {
    console.log('Component activated');
},
deactivated() {
   console.log('Component is disabled');
},
Copy the code

Components into the order

Get the DOM and child component objects

Despite prop and events, there are times when you may need to access a child component directly in JavaScript. To do this, you can use the REF feature to assign an ID reference to the child component. Such as:

const Test = {
    template: '
      
I am testing component
'
} const App = { data() { return{}},created() { console.log(this.$refs.test); //undefined }, mounted() { // If the ref is mounted by the component, get the component object; if the ref is mounted by the tag, get the DOM element console.log(this.$refs.test); console.log(this.$refs.btn); // Load the page so that input automatically gets focus this.$refs.input.focus(); }, components: { Test }, template: `
`
} new Vue({ el: '#app'.data: {},components: { App } }) Copy the code
The use of the nextTick

Defer the callback until after the next DOM update cycle. Use the data immediately after you modify it, and then wait for DOM updates

Something you might not expect is that vue performs asynchronously when updating the DOM. Whenever it listens for data changes,Vue opens a queue and caches all data changes that occur in the same event loop. If the same wather is triggered more than once, it will only be pushed to the queue once. This removal of duplicate data while buffering is important to avoid unnecessary computation and DOM manipulation. Then, in the next event loop, “TICK,” Vue refreshes the queue and performs the actual (de-duplicated) work

<div id="app">
    <h3>{{message}}</h3>
</div>
<script src="./vue.js"></script>
<script>
    const vm = new Vue({
        el:'#app'.data: {message:'123'
        }
    })
    vm.message = 'new Message';// Update data
    console.log(vm.$el.textContent); / / 123
    Vue.nextTick(() = >{
        console.log(vm.$el.textContent); //new Message

    })

</script>
Copy the code

When you set vm.message = ‘new message ‘, the component does not immediately rerender. When the queue is refreshed, the component is updated in the next event loop ‘TICK’. In most cases we don’t need to worry about this process, but if you want to do something based on the updated DOM state, it can be tricky. While vue.js generally encourages developers to think in a “data-driven” way and avoid direct contact with the DOM, sometimes we have to. To wait for Vue to finish updating the DOM after the data changes, use vue.nexttick (callback) immediately after the data changes. This callback will be called after the DOM update is complete.

The application of nextTick

There is a need:

The page pull an interface, this interface to return some of the data, the data is a floating layer components depend on this page, and then I return data in interface shows the floating layer components, showing at the same time, report to some data to the background (these data is the parent component) taken from the interface, the same time, a miracle happened, although I got the data, However, when the floating layer is displayed, the data has not been updated to the component, and the failure is reported

const Pop = {
    data() {
        return {
            isShow:false}},template:` 
      
{{name}}
`
.props: ['name'].methods: { show(){ this.isShow = true; alert(this.name); }}},const App = { data() { return { name:' '}},created() { // Simulate asynchronous request data setTimeout(() = > { this.name = 'Little Pony'.this.$refs.pop.show(); }, 2000); }, components:{ Pop }, template: `<pop ref='pop' :name='name'></pop>` } const vm = new Vue({ el: '#app'.components: { App } }) Copy the code

Perfect solution:

 created() {
     // Simulate asynchronous request data
     setTimeout(() = > {
         this.name = 'Little Pony'.this.$nextTick(() = >{
               this.$refs.pop.show(); })},2000);
},
Copy the code
Note on object change detection

Due to JavaScript limitations,Vue cannot detect the addition and removal of object attributes

Vue does not allow dynamic root-level reactive attributes to be added to already created instances. However, reactive attributes can be added to nested exclusives using the vue.set (Object,key,value) method

<div id="app">
    <h3>
        {{user.name}}{{user.age}}
        <button @click='handleAdd'>Add the age</button>
    </h3>
</div>
<script src="./vue.js"></script>
<script>
    new Vue({
        el:'#app'.data: {user:{},
        },
        created() {
            setTimeout(() = > {
                this.user = {
                    name:'Joe'}},1250);
        },
        methods: {
            handleAdd(){
                console.log(this);
                // No response
                // this.user.age = 20;
                // responsive
                this.$set(this.user,'age'.20); }}})</script>
Copy the code
this.$set(this.user,'age'.20);// It is just an alias for global vue.set
Copy the code

If you want to assign multiple attributes to an existing Object, you can use object.assign ()

// Add multiple attributes in a single response
this.user = Object.assign({}, this.user, {
    age: 20.phone: '113131313'
})
Copy the code

Mixin lazy

Mixins provide a very flexible way to distribute reusable functionality in Vue components. A mixin object can contain any component option.

A mixin object can contain any component option. When a component uses mixin, all mixin options are “blended” into the component’s own options.

<div id="app">
    {{msg}}
</div>
<script src="./vue.js"></script>
<script>
    const myMixin = {
        data(){
            return {
                msg:'123'}},created() {
            this.sayHello()
        },
        methods: {
            sayHello(){
                console.log('hello mixin')}}}new Vue({
        el: '#app'.data(){
            return {
                msg:'Little Pony'}},mixins: [myMixin]
    })
Copy the code

A mixin applied

There’s a rare case where you have two components that are very similar, they share the same basic functions, and they’re different enough that you’re at a crossroads: Do I split it into two different components? Or just use one component and create enough properties to make a difference.

None of these solutions is perfect: if you split it into two components, you run the risk of updating it in both files if the functionality changes, which violates the DRY premise. On the other hand, too many attributes can quickly become confusing and unfriendly to maintainers, or even to yourself, which can frustrate you by requiring an understanding of a large context in order to use it.

Use a mix. Mixing in Vue is useful for writing functional style code, because functional programming is about making code easier to understand by reducing moving parts. Blending allows you to encapsulate a block of functions that can be used in other components of your application. If used correctly, they don’t change anything outside the scope of the function, so if you do it multiple times, you’ll always get the same value as long as it’s the same input. That’s really powerful.

We have a pair of different components that toggle a state Boolean, a modal box, and a prompt box. These prompt boxes and modal boxes have nothing in common except functionality: they look different, use different, but the logic is the same

<div id="app">
    <App></App>
</div>
<script src="./vue.js"></script>
<script>
    // Global mixin is called every time an instance is created
    Vue.mixin({
        created(){
            console.log('hello from mixin!! '); }})/ / pull away
    const toggleShow = {
        data() {
            return {
                isShow: false}},methods: {
            toggleShow() {
                this.isShow = !this.isShow
            }
        }
    }
    const Modal = {
        template: '
       

Modal box component

'
.data() { return{}},mixins:[toggleShow] } const ToolTip = { data() { return{}},template: '

Prompt component

'
.mixins:[toggleShow] } const App = { data() { return{}},template: '
< button@click ='handleModel'> Modal box < button@click ='handleToolTip'> Prompt box
`
.components: { Modal, ToolTip }, methods: { handleModel() { this.$refs.modal.toggleShow() }, handleToolTip() { this.$refs.toolTip.toggleShow() } }, } new Vue({ el: '#app'.data: {}, components: { App }, })
Copy the code