1, Use background
Recently, the company needs to build a new project for its official website. Because as the official website, the first project is not large, there are only a dozen pages in total, and you want a good user experience, so you finally choose Vue as the technology stack.
Although Vue3( Chinese official website )It has just come out, but if Vue2 is used to build the project, it may face the problem of Vue version upgrade in the future. There are many incompatible changes from Vue2 to Vue3. With the development of the times, the use of new technologies will be more maintainable and scalable.
2, Project construction
What I use here is vue cli4 Carry out project construction
3, Installation dependency
Install some common dependencies. It is worth noting that some plug-ins of Vue3 are different from Vue2.
Special note:
-
element-plus : This is a desktop component library based on Vue 3.0.
How to use element plus globally:
// plugins/element.js import ElementPlus from 'element-plus' import 'element-plus/lib/theme-chalk/index.css' import 'dayjs/locale/zh-cn' import locale from 'element-plus/lib/locale/lang/zh-cn' export default (app) => { app.use(ElementPlus, { locale }) } // main.js import installElementPlus from './plugins/element' const app = createApp(App) installElementPlus(app) // use <template> <el-button>Button</el-button> </template>
-
axios : it is worth noting that Vue instances are created through new Vue() in Vue2. There is no concept of app, but in Vue3, createApp is introduced. Therefore, the y reference method of axios is different. For details, please refer to: Vue3 installs axios and reports an error: Uncaught TypeError: Cannot read property 'use' of undefined.
How to use axios:
// Method 1: global mount. This method is not suitable for putting the requests of different modules in the project into corresponding files, // Because we may not be able to get the current instance this when making a request // plugins/axios.js export const plugin = { install: function (app, options) { console.log(options) // Add global method app.config.globalProperties.axios = _axios; } } // main.js import { plugin as axios } from './plugins/axios' const app = createApp(App) app.use(axios) //use: this.$axios.post('api/Login',{card:111}).then(res => console.log(res)) //Mode 2: direct use // plugins/axios.js const _axios = axios.create(config) export default _axios // use: import axios from '@/plugins/axios' // Get position category export function getJobCategories() { return axios.post('api/Login',{card:111}) }
- Vue Router : Vue router4 also provides hook hook functions for use.
Using router in setupimport { useRouter } from "vue-router" const router = useRouter() router.push('/')
4, What's new in Vue3
1. Combined API
What is a composite API?
To spread vue functions in a file is simply to put them together
This will make our code more clustered. Students who have used react functional components should find that they are very similar.
-
Setup: setup is the entry point of the composite api. All functions can be written in setup. The parameters that need to be used outside setup are exposed through return
setup receives two parameters: props and context.Props accepts the parameters passed from the parent component. Of course, these parameters are also responsive. It is worth noting that when props is used in setup, the variables defined in props can only be received by props in setup. When obtaining the variables of props in setup, the deconstruction method of es6 can not be used, because it will eliminate the responsiveness of props. If it is necessary to deconstruct props and preserve the response formula, the toRefs function in setup function can be used.
context exposes the properties of three components: attrs (Attribute), slots (slot) and emit (trigger event). context is an ordinary JavaScript object, which does not have a response, and can use es6 deconstruction syntax.
be careful ⚠️
Setup is called before parsing other component options, so the current component instance: this cannot be accessed accurately in setup.
To get the current component instance in setup, the official provides a function: getCurrentInstance, but getCurrentInstance can only be used in setup and life hook functions
// getCurrentInstance represents the global context. ctx is equivalent to this of Vue2, // But pay special attention that ctx instead of this is only applicable to the development stage. When you put it on the server, you will make an error, // Later, according to the information, proxy must be used to replace ctx in order to run normally in the officially launched version of your project const { ctx, proxy } = getCurrentInstance()
- Life cycle hook
- Provide / Inject: parent-child components transmit data across levels
// Parent component setup () { // After the readonly package is wrapped, it can be referenced in the component without changing the value. // Otherwise, in the component, you can directly use applypositionid Value = * * * change value provide('applyPositionId', readonly(applyPositionId)) // Functions can also be passed provide('hideApplyModal', hideApplyModal) } //Sub component reception setup() { const applyPositionId = inject('applyPositionId') const hideApplyModal = inject('hideApplyModal') // function call hideApplyModal() }
2. Responsiveness API
In Vue2, the data defined in data will be used by object Defineproperty () hijacks data and becomes responsive data through observer mode.
In Vue3, the way to realize the response is changed to the new syntax proxy in es6, and the response is realized through object interception, which solves some leakage areas in vue2.
There is a lot of relevant knowledge about these two points. I will write a new article to explain them accordingly. Here we mainly introduce the use of some new features of Vue3.
-
reactive
reactive is used to create a responsive state for an object, which is equivalent to Vue 2 Vue in X observable(). After creating a responsive state for an object, there will be no problem of modifying the attributes of an object that are not defined when data is defined in Vue2, and there will be no problem of responsive state.
setup() { const obj = { count: 0} const state = reactive(obj) console.log(state.count) // 0 }
There is a sentence on the official website:
Responsive transformation is "deep" - it affects all nested properties. What does that mean?When we use proxy (those who don't know proxy can read: Ruan Yifeng es6 beginner level )When doing object interception, he will hijack all property properties of this object.
vue2 uses Vue Observable () enables an object to respond, and the incoming object will be directly Vue Observable is changed to responsive. reactive is to create a responsive copy of the incoming object without changing the original object. When we directly change the original object, the original object will not be responsive. -
readonly
readonly gets an object (reactive or pure) or ref and returns the read-only proxy of the original proxy
const state = reactive({ count: 0 }) const copy = readonly(state) //error copy.count++
-
ref
This ref is not that Ref. both Vue2 and react have the concept of Ref. When its scope is HTML file, ref is the interface provided by React/Vue to manipulate the instance of React/Vue component or DOM element.
In the responsiveness API, ref is used to create a responsive and variable ref object, which has a pointer to the variable it creates Value attribute. When return ed in setup, its value will be bound automatically.
setup() { const count = ref(0) console.log(count.value) // 0 return { count } } mounted() { console.log(count) // 0 }
There is a sentence on the official website:
After reading it for a long time, I don't particularly understand what it means. After using ref and reactive, it is found that they are not much different except in the way they are used. Highly responsive thoughts are still not realized. Through the source code, I can see that when using ref to create responsive data for an object, the reactive method is also called internally.
If you have any understanding, you can comment and let me know. 🙏🙏 -
computed
The use of computed is very simple. In essence, it is not much different from vue2
-
watch
This is equivalent to vue2$ Watch, which can listen to one or more sources. Execute when the dependent value changes.
// Listen for a getter const state = reactive({ count: 0 }) watch( () => state.count, (newcount, prevCount) => { console.log('New value:', newcount) console.log('Old value:', prevCount) } ) // Listen directly to a ref const count = ref(0) watch(count, (newcount, prevCount) => { console.log('New value:', newcount) console.log('Old value:', prevCount) }) // Listen to multiple sources, using array form const fooRef = ref(0) const barRef = ref(1) watch([fooRef, barRef], ([newFoo, newBar], [prevFoo, prevBar]) => { console.log('new Foo value:', newFoo) console.log('used Foo value:', prevFoo) console.log('new Bar value:', newBar) console.log('used Bar value:', prevBar) })
-
watchEffect
watchEffect runs a function immediately when it responsively tracks its dependencies and reruns it when the dependencies are changed: it means that the page will be executed once at initialization and again when the dependencies are changed. Students who are familiar with React Hook will be very familiar with it. It has the same function as useEffect. The only difference is that useEffect requires us to manually pass in dependencies, while watchEffect will automatically collect dependencies.
const count = ref(0) watchEffect(() => console.log('value:', count.value)) // value:0 setTimeout(() => { count.value++ // value:1 }, 100)
4, Incompatible changes
Vue3 there are many incompatible changes on Vue2. Here are some common attributes.
-
v-model
The v-model in Vue2 is equivalent to the syntax sugar of: value + input.
The v-model in Vue3 is equivalent to: modelValue + update:modelValue.// Vue2 <ChildComponent v-model="pageTitle" /> <!-- Equivalent to: --> <ChildComponent :value="pageTitle" @input="pageTitle = $event" /> // Vue3 <ChildComponent v-model="pageTitle" /> <!-- Equivalent to: --> <ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle = $event" />
In Vue2, if we want to change the value passed down from v-model, we need the model option to change it.
// parentComponent <ChildComponent v-model="pageTitle" /> // ChildComponent export default { model: { prop: 'title', event: 'change' }, props: { // This will allow the 'value' attribute to be used for other purposes value: String, // Use 'title' instead of 'value' as the prop of the model title: { type: String, default: 'Default title' } } } // perhaps <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" /> // perhaps <ChildComponent :title.sync="pageTitle" /> // Subcomponents this.$emit('update:title', 'new title')
In Vue3, V - bind is removed Sync modifier. The above functions are abbreviated as:
<ChildComponent v-model:title="pageTitle" /> // Equivalent to <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" /> Sub components: setup(props, context) { const { emit } = context || {} const onClose =() => { emit('update:pageTitle', 'new title') } return { onClose, } }
-
v-if and v-for
In Vue2, when v-if and v-for act on the same DOM node at the same time, v-for takes precedence over v-if.
In Vue3, when v-if and v-for act on the same DOM node at the same time, the priority of v-if is higher than that of v-for.
And the key attribute will continue to be used in the branches of v-if/v-else/v-else-if, because when no key is provided for the conditional branch, a unique key will be automatically generated. -
filters
filters in Vue3 have been completely removed. It is recommended to use computed instead.
-
template
In Vue2, only one root node is supported in the template, otherwise an error will be reported.
In Vue3, the template only supports multiple root nodes. -
prop
In Vue2, we need to access the fields in data in prop. You can use this.
In Vue3, this is no longer supported in prop.
4, Customize Hook
-
useFetch
import { reactive, toRefs } from 'vue' export default function useFetch(api, params, transformer) { if (typeof api !== 'function') { throw new TypeError('api should be type of fuction') } // Define initial state const state = reactive({ data: null, loading: false, }) // Define query function const doFetch = (otherParams) => { const finalParams = { ...(params || {}), ...(otherParams || {}) } state.loading = true return api(finalParams).then((data) => { state.data = typeof transformer === 'function' ? transformer(data) : data state.loading = false return data }) .catch((err) => { console.log(err && err.message) state.loading = false }) } // Return status refs and query function return [toRefs(state), doFetch] } // use import useListFetch from '@/hooks/useListFetch' setup() { const getdataFunc = (params) => { const { id } = params || {} return axios.get(`/xxx?id=${id}`) } const [ { data, loading: isLoading}, getData, ] = useListFetch(getdataFunc, { id: 10, }) getData() }
-
useListFetch
import { reactive, toRefs } from 'vue' export default function useListFetch(api, params, transformer) { if (typeof api !== 'function') { throw new TypeError('api should be type of fuction') } // Define the initial state of the list const state = reactive({ items: [], loading: false, isLastPage: false, }) const { pageSize = 10, ...otherParams } = params || {} // Define query function const doFetch = () => { const preSize = state.items.length const finalParams = { ...otherParams, offset: preSize, limit: pageSize, } state.loading = true return api(finalParams).then((data) => { if (data && Array.isArray(data)) { const newData = typeof transformer === 'function' ? transformer(data) : data const newItems = [...state.items, ...newData] state.items = newItems if (newItems.length !== preSize + pageSize) { state.isLastPage = true } } state.loading = false return data }) .catch((err) => { console.log(err && err.message) state.loading = false }) } // Return status refs and query function return [toRefs(state), doFetch] } // use import useListFetch from '@/hooks/useListFetch' setup() { const getListFunc = (params) => { const { offset = 0, limit = 10 } = params || {} return axios.get(`/xxx?offset=${offset}&limit=${limit}`) } const [ { items: videoList, loading: isLoading, isLastPage: isLastPage }, getList, ] = useListFetch(getListFunc, { pageSize: 10, }) getList() }
5, Project packaging
Reference documents: https://cli.vuejs.org/zh/guide/
These are some important changes when I use Vue3.