编程知识 cdmana.com

Analysis of new features of vue3

summary :1. How to initialize vue3 project 2. Support multiple root nodes 3. Entrance file mian.js Changes 4. better typescript Support 5. automatic tree-shaking 6. Portal teleport 7. composition api 8.setup 9.ref 9.reactive 10.readonly 11.computed 12.watch 13.watchEffect 14. Lifecycle hook 15. Dependency injection 16.refs 17. Code organization 18. Logic reuse

initialization vue3 project

  • Method 1 : adopt vue-cli Create directly , The latest version of scaffold is required
  • Method 2 : In an initialized vue2 In the project , Use commands in the terminal vue add vue-next To upgrade to vue3 project .( Note that in an initialization vue2 project , Don't make a fool of )

Support multiple root nodes

  • We all know that vue2 Only allowed in template Define a root node in the , Otherwise The template root requires exactly one element The error is reported in the prompt and results in a compilation error .
  • stay vue3 We can do it in template Write multiple root nodes in it , This feature is available in vue3 To show .
<template>
  <div>hello</div>
  <div>world</div>
</template>
 Copy code 

Entrance file mian.js

  • Let's start with vue2 in main.js Code introduction :
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
	render:h=>{App}
    
}).$mount('#app')
 Copy code 
  • Let's go back to vue3 in main.js Code introduction :
import { createApp } from "vue";
import App from "./App.vue";

// Vue3 Most of the ways to use the function to achieve 
// App yes  vue  Component object instance of 
createApp(App).mount("#app");



/*App yes vue Component object instance of , The structure is as follows :
{
  name: 'App',
  components: {
    HelloWorld
  }
}
*/
 Copy code 

better typescript Support

  • vue3 Because a typescript Support for , coordination vscode You can write code with very good type hints , The development experience is very good

Support for automatic tree-shaking

  • stay vue3 Will automatically package the build code tree-shaking. For users You don't need vue The function doesn't need to be packed in. It will optimize the final file volume ; For framework developers Can provide more functions, but also do not have to worry about will cause the user's file volume to increase ( If the user does not use the function )

The concept of portal

  • vue3 Added portal teleport The concept of , Let's look at a business scenario ( stay vue2 Next ), And use vue3 To solve this business scenario , The official demo address
  • stay vue2 Because only one root node is allowed , The code is written as follows :( Notice the root node in the code below #app Of style="position: relative;")
// Here it is. App.vue The code in :
<template>
  <div id="app" style="position: relative;">
    <h3>Tooltips with Vue 3 Teleport</h3>
      <modal-button></modal-button>
  </div>
</template>

<script>
import ModalButton from "./components/ModalButton";
export default {
  name: "App",
  components: {
    ModalButton,
  },
};
</script>
 Copy code 
  • We are in subcomponent ModalButton I want to achieve a Based on the pop-up window centered , The code is written as follows :( Notice the .modal Of style="position: absolute;")
<template>
  <button @click="modalOpen = true">
    Open full screen modal! (With teleport!)
  </button>
  
    <div v-if="modalOpen" class="modal">
      <div>
        I'm a teleported modal! (My parent is "body")
        <button @click="modalOpen = false">
          Close
        </button>
      </div>
    </div>
</template>

<script>
export default {
  data() {
    return {
      modalOpen: false,
    };
  },
};
</script>

<style>
.modal {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.modal div {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: white;
  width: 300px;
  height: 300px;
  padding: 5px;
}

</style>
 Copy code 
  • So we get the root node based #app In the middle of the box , This is not the result we want , We want to be window based ( This could be body) The center of the box !!!, The renderings are as follows :

  • Then it's time to invite us out vue3 New features of portal (teleport) 了 , The following is to use vue3 Rewrite in subcomponents ModalButton The code in , Refer to the following :
<template>
  <button @click="modalOpen = true">
    Open full screen modal! (With teleport!)
  </button>

  <teleport to="body">
    <div v-if="modalOpen" class="modal">
      <div>
        I'm a teleported modal! (My parent is "body")
        <button @click="modalOpen = false">
          Close
        </button>
      </div>
    </div>
  </teleport>
</template>

<style>
.modal {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.modal div {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: white;
  width: 300px;
  height: 300px;
  padding: 5px;
}

</style>
 Copy code 
  • Using the portal <teleport to="body"> </teleport> The effect picture is as follows , We have achieved the goal based on body In the middle of the pop-up window :

  • We need to pay attention to the use of <teleport to="body"> </teleport> After rendering, it is not rendered to the component currently introduced into the component, but to the specified element Inside
  • We also need to be careful teleport Can be used multiple times in a page component , But we're using multiple teleport You need to pay attention to the z-index Relationship , This is an add operation .
  • We need to pay more attention to If I were in teleport Add sub components to the , Who is the parent component of this child component at this time ? The parent component of the child component defined in the gate belongs to the component that introduced the component , Here I'm at the component ModalButton Used in teleport, If this teleport Sub components are also defined in , So this is in teleport The parent component of the child component defined in is ModalButton!!!
// Here it is. ModalButton Code written in the component of 
// Below it is written teleport Components in Foo The parent component of is ModalButton

<template>
  <button @click="modalOpen = true">
    Open full screen modal! (With teleport!)
  </button>
  <teleport to="body">
    <div v-if="modalOpen" class="modal">
      <Foo></Foo>
      <div>
        two
      </div>
    </div>
  </teleport>
</template>

<script>
import Foo from "./Foo";
export default {
  components: {
    Foo,
  },
  data() {
    return {
      modalOpen: false,
      lth:true
    };
  },
};
</script>
 Copy code 

composition api

  • And then there was vue3 The play of , That's it composition api. This can be said to completely subvert the use of vue The logical writing habit of , In order not to introduce new concepts , Independent functions are used to create and monitor responsive states
  • We don't have to worry about vue2 Of options api Can it be in vue3 Use in , It and vue3 Of composition api It's compatible and coexisting , But I suggest a unified style of writing .
  • The following will composition api Each api Write it as a headline alone :

setup entrance

  • setup It's the entry to initialization , Represents the current component initialization composition api Entrance , stay setup Function return The value in the object can be written directly in template in , But be aware that if you don't do anything about it , Directly use the returned value in template It's written in Zhongshu , This value is not responsive data !!!
  • Let's take a look setup The use of entrance , The code reference is as follows :
<template>
<div id='Setup'>
     Not reactive data :{{count}}
    <button @click="handleClick"> Click button count</button>
    <hr/>
</div>
</template>

<script>
export default {
    name: 'Setup' ,
    setup(){
    	console.log(this,' there this yes undefined')
    	const count =1;
        const handleClick=function handleClick(e){
            console.log(e,'e');
            console.log(' It's not reactive data anymore ?');
            // 'count' is constant  no-const-assign
            //  The next line is wrong 
            // count++;
        };
        
        // An object must be returned 
        // The returned data is exposed to template With 
        // If the returned data is not processed in template Direct use is not reactive data 
        return {
          // Returns a normal value 
          count,
          // Returns a function 
          handleClick
        }
    }
}
</script>
 Copy code 
  • stay setup Function return The value in the object can be written directly in template in , Notice that it has to return an object , If you return a function , It's actually a call render The function returns a vnode.
// stay setup in return Can be a function  
return ()=>{
	// Here it's equivalent to render function 
    //return  vnode
    // Return a virtual node 
    // It's not recommended 
}
 Copy code 
  • Because in data You can get setup The value returned , So you can go straight to template Use this return value in .
  • Pay attention to the setup Function this It's impossible to get , because setup The function performs better than beforeCreate and created The call time should be early , It's in options api Previously called , Nature can't get
  • setup The first parameter of props,( namely setup(props){}) You can get the props, Of course, this is passed from the parent component props It can't be modified in a sub component , Is a read-only property .

ref Use

  • ref It's a reactive system api One of the top generals of , In the example above, we can find that we return without any processing count Not reactive data , It's just a normal value , But what we need is when I click the button count Data can be accumulated as a response
  • ref Can be used to create a responsive data , The code reference is as follows :
<template>
<div id='Setup'>
     It's reactive data :{{count1}}
    <button @click="handleClickCount1"> Click button count1</button>
     It's no use writing like this {{count1.value}}
    <hr/>
</div>
</template>

<script>
// All of these functions have to be in setup Function to call 
import {ref} from 'vue';
export default {
    name: 'Setup' ,
    setup(){
        // Create a responsive data 
        const count1=ref(0)
        //RefImpl object 
        //console.log(count1,'count1'); 

        const handleClickCount1=function handleClickCount1(){
            console.log(' It's responsive data ');
            console.log(this,' I can get it here this');
            // Be sure to remember to pass through .value To take a value 
            count1.value++;
        };

        
        // An object must be returned 
        // The returned data is exposed to template With 
        // If the returned data is not processed in template Direct use is not reactive data 
        return {
          // Returns a normal value 
          count1,
          // Returns a function 
          handleClickCount1
        }
    }
}
</script>
 Copy code 
  • Be careful : Through here const count1=ref(0) Created a value of 0 Response data for , And in handleClickCount1 In this method, the response data accumulation method is realized .
  • What we need to pay attention to here is count1 Now it's a RefImpl object ,0 This value is stored in the object's value Attribute , So we are setup In the function, you need to pass the count1.value To get this value 0, This puts a lot of mental burden on us .
  • Although we are setup In the function, you need to pass the count1.value To get this value 0, But we can go straight back to this count1 object , And in template You can use {{count1}} To get this value

reactive Use

  • ref It's a reactive system api One of the top generals of , In the example above, we use ref Create a responsive data , We can also go through reactive To create a responsive data , The code reference is as follows :
<template>
<div id='Setup'>
    <button @click="handleAge"> Click the button to change age</button>
    user:{{user}}user name:{{user.name}}user age{{user.age}}
    <hr/>
</div>
</template>


<script>
// All of these functions have to be in setup Function to call 
// It has to be introduced before it can be used 
import {reactive} from 'vue';

export default {
    name: 'Setup' ,
    setup(){
 		
        // Reference type   object / Array /......
        let user=reactive({name:'lth',age:20})
        // Proxy {name: "lth", age: 20}
        // console.log(user,'user');
        
      	const handleAge=()=>{
            console.log('handleAge');
            console.log(user.age,'user.age before');
            user.age++;
            console.log(user.age,'user.age after');
        }
        
        
        // An object must be returned 
        // The returned data is exposed to template With 
        // If the returned data is not processed in template Direct use is not reactive data 
        return {
          // Returns a reference type 
          user,
          // Returns a function 
          handleAge
        }
    }
}
</script>
 Copy code 
  • Be careful : Through here let user=reactive({name:'lth',age:20}) Create a responsive data reference type , And in handleAge The right user.age Accumulation .
  • We need to pay attention to the user yes Proxy object , You can go directly through user.age and user.name To access property values directly , This has to be done and used ref Distinguish .

readonly Use

  • readonly It's a reactive system api One of the top generals of , In the above example, we used reactive Create a responsive data , We can see that when you click the button ,user.age The value of is added up , That is to say user.age The value of can be modified , If we want a value to be both responsive and immutable, read-only ? We can use readonly, The code reference is as follows :
<template>
<div id='Setup'>
    <button @click="handleReadOnly"> read-only age</button>
    {{readonlyUser}}
</div>
</template>


<script>
// All of these functions have to be in setup Function to call 
import {readonly} from 'vue';
export default {
    name: 'Setup' ,
    setup(){
    
    
      const readonlyUser=readonly({user1:'secret',age:20})
      // console.log(readonlyUser,'read');
      
      const handleReadOnly=()=>{
        // The next two lines are completely unresponsive 
        readonlyUser.age++
        console.log(readonlyUser.age=23,'ss');
      }
        
        // An object must be returned 
        // The returned data is exposed to template With 
        // If the returned data is not processed in template Direct use is not reactive data 
        return {
          // Returns a read-only value 
          readonlyUser,
          // Returns a function 
          handleReadOnly
        }
    }
}
</script>
 Copy code 
  • Let's look back at vue2 From the parent component to the child component props Properties are also reactive , But we can't modify it directly in a subcomponent props Data , It's also a read-only property .
  • stay vue3 Directly to the received in the subcomponent props Automatically set readonly, If you try to modify it directly in a subcomponent props attribute , It's a mistake , The code is as follows :
<script>
// All of these functions have to be in setup Function to call 
import {readonly} from 'vue';
export default {
    name: 'Setup' ,
    props:{
      msg:String
    },
    setup(props){
   		
        //proxy object  readonly
        // console.log(props,'props');  
        
        //Set operation on key "msg" failed: target is readonly.
        // console.log(props.msg='12456');
        
        
        return {
          //todo...
        }
    }
}
</script>
 Copy code 

computed Use of calculation properties

  • computed It's a reactive system api One of the top generals of , The computed property here receives a corresponding function , Let's review a computational property , It's an attribute that depends on other properties , By returning a property for the template to call
  • By calculating properties, you create responsive data , Go straight to the code :

<template>
<div id='Setup'>
     It's reactive data :{{count1}}
    doublecount:{{double}}
   <button @click="handleClickCount1"> Click button count1</button>
</div>
</template>


<script>
// All of these functions have to be in setup Function to call 
import {computed,ref} from 'vue';
export default {
    name: 'Setup' ,
    setup(){
    	const count1=ref(0)
        
        // The computed property receives a corresponding function 
        const double=computed(()=>{
            return count1.value*2
        })
        
        //ComputedRefImpl  
        // Computed properties also create responsive data 
        // and ref The type is consistent, basically the same 
        // Need to pass through double.value To get specific values 	
        // console.log(double,'double');
 		
      	const handleClickCount1=function handleClickCount1(){
            console.log(' It's responsive data ');
            console.log(this,' I can get it here this');
            // Be sure to remember to pass through .value To take a value 
            count1.value++;
        };
        
        return {
          // Returns a value 
          count1,
          double,
          // Returns a function 
          handleClickCount1,
        }
    }
}
</script>
 Copy code 

watch The use of listeners

  • watch It's a reactive system api One of the top generals of ,watch The first parameter of must be responsive data or a function that returns responsive data . Go straight to the code :
<script>
// All of these functions have to be in setup Function to call 
import {watch,reactive,ref} from 'vue';
export default {
    name: 'Setup' ,
    setup(){
    	const count1=ref(0)
        
        // Pay attention to the order of writing 
        // Not yet count1 Just watch You can't 
        watch(count1,(newValue,oldValue)=>{
            console.log(newValue,oldValue,'newValue,oldValue');
        },{
            immediate:true
        })
        
        
        let user=reactive({name:'lth',age:20})
        
        //user It's responsive data 
        watch(user,(newValue,oldValue)=>{
            console.log(newValue,oldValue,'newValue,oldValue');
        },{
        immediate:true
        })
        
        
        // A function that returns responsive data 
      	watch(()=>user.age,(newValue,oldValue)=>{
            console.log('fuck anyone');
            console.log(newValue,oldValue,'newValue,oldValue');
        })
        
        return {
			user,
            count1
        }
    }
}
</script>
 Copy code 

watchEffect Use

  • watchEffect It's a reactive system api One of the top generals of , It doesn't need to be set up in the beginning to see who , Always get the latest value of the value written in the modification function , Receive a function directly , And at the beginning, it was executed immediately . The code is as follows :
<script>
// All of these functions have to be in setup Function to call 
import {watchEffect,reactive} from 'vue';
export default {
    name: 'Setup' ,
    setup(){
      let user=reactive({name:'lth',age:20})

        // Receive a function directly 
        // You don't need to set up at the beginning to see who 
        watchEffect(()=>{
            console.log('fun k');
            // When user.age It will trigger 
            // And it was executed immediately as soon as it came up 
            console.log(user.age);

        })

        
        return {
          user
        }
    }
}
</script>
 Copy code 

Life cycle hook changes

  • setup The function performs better than beforeCreate and created The call time should be early , It's in options api Previously called , The following table shows the changes of life cycle hooks :
beforeCreate Use setup()
created Use setup()
beforeMount Use onBeforeMount
mounted Use onMounted
beforeUpdate Use onBeforeUpdate
updated Use onUpdated
beforeDestroy Use onBeforeUnmount
destroyed Use onUnmounted
errorCaptured Use onErrorCaptured

The use of dependency injection

  • Dependency injection (provide/inject) Can be used for component communication , Let's start with APP.vue The code in :
  • provide The first parameter of key, The second parameter is value
<template>
  <div>
    <Bar></Bar>
  </div>
</template>

<script>
import Bar from "./components/Bar";
import { provide } from "vue";

export default {
  name: "App",
  components: {
    Bar,
  },
  setup() {
    provide("app", "app-component");
  },
};
</script>
 Copy code 
  • Let's look at the subcomponents Bar The code in :
import {inject} from 'vue';
setup() {

  // inject The second parameter of can specify a default value 
  //  stay provide('app','app-component') When the second parameter is not passed 
  //const app=inject('app',' The default value is used when it is not transmitted ')

  const app=inject('app');
  console.log(app,'app');
}
 Copy code 

refs Use

  • Use refs You can get the truth dom Element or component instance , The code reference is as follows :
<template>
<div id="App">
    hello world
    <button ref="btn"> Click on refs</button>
</div>
</template>


<script>
import {onMounted,provide} from 'vue';
import Setup from './components/Setup';
export default {
  name: 'App',
  
  setup() {
    onMounted(()=>{
      console.log(btn);
    })

    const btn=ref(null);

    return {
      // Back to btn Be sure to make contact with 
      //<button ref="btn"> Click on refs</button>
      // Medium btn Agreement 
      btn
    }
  }
}
</script>
 Copy code 

Code organization

  • Why use it composition api Well , Because it can achieve better Code organization , Let's look at the past in vue2 The regular code written in Chinese books :
export default {
    name: 'Foo' ,
    components: {},
    data() {
        return {
            one:1,
            tow:2
        }
    },
    computed: {
        oneCom() {
            return this.one
        },
        twoCom() {
            return this.two
        }
    },
    methods: {
        oneF() {
            console.log('11');
        },
        twoF() {
            console.log('22');
        }
    },
    watch: {
        one(newValue, oldValue) {
            console.log(newValue, oldValue);
        },
        two(newValue, oldValue) {
            console.log(newValue, oldValue);
        }
    },
};
 Copy code 
  • The code organization above seems redundant , Different logic points are put in different places , Less logic is more clear , But if there is too much logic, it will be troublesome , We're looking to change it into composition api Post writing ( The following is not the best way to write , The best way to write it is below )
    setup() {
        // Function one 
        const one =ref('one')
        const oneF=()=>{console.log('one')};
        const oneCom=computed(()=>{return one.value*2});
        watch(one,()=>{return });

        // Function 2 
        const two =ref('two')
        const twoF=()=>{console.log('two')};
        const twoCom=computed(()=>{return two.value*2});
        watch(two,()=>{return });
    }
 Copy code 
  • In the above writing, we can clearly observe the use of a function block , We're here to make some changes , Separate the function block into a function according to the function , Using the idea of combination , The code is as follows :
<template>
<div id='Foo'>
    foo
    <hr/>
    <button @click="oneF"> Click Change one</button>
    this is one:{{one}}
</div>
</template>

<script>
import {computed,watch,ref} from 'vue';
export default {
    name: 'Foo' ,
    components: {},

    setup() {
        // Function one 
        const {one,oneF} =oneFeature()

        // Function 2 
        const {two,twoF} =twoFeature()


        return {
            one,
            oneF
        }
    }
};


function oneFeature(){
    const one =ref(1)
    const oneF=()=>{console.log(one);one.value++};
    // const oneCom=computed(()=>{return one.value*2});
    // watch(one,()=>{return });

    return {
        one,
        oneF
    }
}

function twoFeature(){
    const two =ref(2)
    const twoF=()=>{console.log(two)};
    // const twoCom=computed(()=>{return two.value*2});
    // watch(two,()=>{return });

    return {
        two,
        twoF
    }
}
</script>
<style lang="scss" scoped>
</style>
 Copy code 
  • We can clearly see the simplicity brought by the above writing , And we can put the function block function above Put it alone in a js Export in file , The derived function is introduced by the main entry , This is the best solution , The code is as follows :
// Because of all the composition api Can be used as a function of the export 
import {computed,watch,ref} from 'vue';


// In the js The function block method is encapsulated in the file and exported 
export  oneFeature(){
    const one =ref(1)
    const oneF=()=>{console.log(one);one.value++};
    // const oneCom=computed(()=>{return one.value*2});
    // watch(one,()=>{return });

    return {
        one,
        oneF
    }
}
 Copy code 

Logic reuse

  • We know that a function actually represents reusable , stay vue2 When we want to reuse components, we often use mixins To achieve , The code reference is as follows :
// Use mixins If the source is not clear, it is easy to cause naming conflict 
// Here is 


<template>
  <div>{{ x }} -- {{ y }}</div>
</template>

<script>

// The following is the MoveMixin.js Need to be encapsulated in mixins Reuse components of 
import { MoveMixin } from "./MoveMixin";


export default {


  mixins: [MoveMixin],
  
  // If there are multiple reusable components ?
  // problem : Unclear source and easy to cause naming conflicts 
  //mixins: [MoveMixin, fooMixin, barMixin],

  },
};
</script>
 Copy code 
  • The following is the ./MoveMixin.js The logic code of the reuse component of :
export const MoveMixin = {
  data() {
    return {
      x: 0,
      y: 0,
    };
  },

  methods: {
    useMousePosition(e){
      this.x=e.pageX;
      this.y=e.pageY;
    }
  },

  mounted() {
    window.addEventListener("mousemove", this.useMousePosition);
  },
	
  // Because in vue3 So it's written here  unmounted()
  // It can be changed back to vue2 Of 
  unmounted() {
    window.removeEventListener("mousemove", his.useMousePosition);
  },
};
 Copy code 
  • If we have multiple reusable components that need to be used , Use mixins The problem of unclear source and naming conflict is very difficult , We are looking at vue3 The benefits of logical reuse for us , The code reference is as follows :
// Use it here vue3 Encapsulating the logic of reusable components 
// The following is the useMousePosition.js The code in 
// Here is js file 

import { onMounted, onUnmounted, reactive } from "vue";


export function useMousePosition() {

  const position = reactive({
    x: 0,
    y: 0,
  });

  const useMousePosition = (e) => {
    // console.log(e);
    position.x=e.pageX
    position.y=e.pageY
  };

  onMounted(() => {
    window.addEventListener("mousemove",useMousePosition);
  });

  onUnmounted(() => {
    window.removeEventListener("mousemove", useMousePosition);
  });

  return { position };
}
 Copy code 
  • Use veu3 After encapsulating the logic of reusable components , The code used in the component is as follows :
  • There's a hole below , It's the response data returned. If you deconstruct it directly , The deconstructed values are no longer responsive data , have access to toRefs To solve this problem .
  • We can find that our logic reuse is very clear .
template>
<div id="App">
     Mouse position x:{{x}} Mouse position y:{{y}}
</div>
</template>

<script>
import {useMousePosition} from './components/useMousePosition';


import { toRefs } from "vue";
export default {
  name: 'App',
  setup() {
    const {position}=useMousePosition();
    
    
    //  Responsive data objects are missing 
    // x and y No more responsive data 
    // const { x, y } = position;
    // console.log(x,y); // Common value 


    //const { x, y } = toRefs(position);
    //ObjectRefImpl  It becomes responsive data again 
    //console.log(x,y);  
    
    
    //position Is in useMousePosition.js Create responsive data 
    // If we directly deconstruct x and y Will lose the responsive 
    // You can use toRefs So deconstructed x and y It's still responsive data 
    const {x,y}=toRefs(position);
    
    return {
      x,
      y
    }
  }
}
</script>
 Copy code 

版权声明
本文为[Linzijiang]所创,转载请带上原文链接,感谢
https://cdmana.com/2020/12/20201224152607766v.html

Scroll to Top