Before you know it, VUe3.0 has entered the beta version, and the official version is coming soon. I have read a lot of articles about Vue3.0 before, but the paper is too shallow, now I will write a handwritten demo!!
The first step is to set up the environment. Although the latest version of @vue/ CLI3 already supports the creation of VUe3.0 projects, but do it yourself. Just review the configuration of WebPack.
The source of the article I put on Github, can be obtained by themselves.
Environment set up
Initialize the
Mkdir vue3.0-demo CD vue3.0-demo // NPM init -y // git initialize git initCopy the code
Add the gitignore file
Create a gitignore file and copy it with the following contents:
.DS_Store node_modules/ /dist/ npm-debug.log* yarn-debug.log* yarn-error.log* /test/unit/coverage/ /test/e2e/reports/ /build/ selenium-debug. Log # Editor directory and file.idea.vscode *.suo *.ntvs* *.njsproj *.slnCopy the code
Install dependencies
// Install vue3.0 yarn add vue@next // Install webpack yarn add webpack webpack-cli webpack-dev-server -d // Install package and compile dependency yarn add Html-webpack-plugin clean-webpack-plugin -d // Install vue file compile dependency yarn add vue-loader@next @vue/compiler-sfc -d // Install style compile dependency yarn add css-loader style-loader less-loader -DCopy the code
Create a project directory
// Create the related project folder mkdir SRC // create the public entry folder mkdir public // create the webpack configuration file CD > webpack.config.js // Create the base file CD > SRC /main.js CD > public/index.html cd > src/App.vueCopy the code
Write index. HTML
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<title>Vue3.0 demo</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
Copy the code
Configuration webpack. Config. Js
Is a very common configuration file, if you really do not understand, you can first look at the official Webpack documentation.
const path = require('path')
// Vue-loader 15.x must use this plug-in
const { VueLoaderPlugin } = require('vue-loader')
// Write style and js files to index.html via link and script
const HtmlWebpackPlugin = require('html-webpack-plugin')
// Delete the packaged files
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const resolve = dir= > path.resolve(__dirname, dir)
module.exports = (env = {}) = > ({
// The current operating mode
mode: env.prod ? 'production' : 'development'.// Debug tools
devtool: env.prod ? 'source-map' : 'inline-source-map'.// Pack the entry
entry: resolve('./src/main.js'),
// Package export
output: {
path: resolve('./dist'),
publicPath: '/'
},
// Parse the module request
resolve: {
alias: {
'vue': '@vue/runtime-dom'.The '@': resolve('./src')}},// Module configuration, various loader
module: {
rules: [{test: /\.vue$/,
use: 'vue-loader'
},
{
test: /\.css$/,
use: ['style-loader'.'css-loader'] {},test: /\.less$/,
use: ['style-loader'.'css-loader'.'less-loader']]}},/ / the plugin
plugins: [
new VueLoaderPlugin(),
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: resolve('./public/index.html'),
filename: 'index.html'})].// Development server
devServer: {
publicPath: '/'.inline: true.hot: true.stats: 'minimal'.contentBase: __dirname,
overlay: true.historyApiFallback: true}})Copy the code
Write the webpack entry file main.js
The mount mode of VUe3.0 is slightly changed
import { createApp } from 'vue'
import App from './App.vue'
// Create an application
const app = createApp(App)
/ / a mount
app.mount('#app')
Copy the code
Write the app.vue file
Written as a new feature of Vue3.0, the template can directly contain multiple child nodes without the need for additional element tags
<template>
<h1>I am a Home page</h1>New to Vue3.0, template allows multiple tag nodes without the need for an additional layer</template>
<script>
export default {
name: 'App'
}
</script>
Copy the code
Add the script to package.json
// 添加脚本
"scripts": {
"dev": "webpack-dev-server"
}
Copy the code
Start the project
Execute the following command:
yarn dev
// or
npm run dev
Copy the code
After successful startup, visit http://localhost:8080/ and you will see the following content in your browser.
composition API
The Composition API borrows ideas from React hooks to make business logic more cohesive. Principles aside, API engineers only care about how well the API works. An important feature of Vue3.0 is the Composition API, so let’s take a look at setup, REF, Reactive, computed, toRefs, and watchEffect with the demo. More details can be found in the draft.
setup
Features of the setup function:
- Is the entry point to the Composition API;
- Called before the cycle beforeCreate event is declared;
- Returns an object whose properties are incorporated into the render context and can be used directly in the template;
- We can return a render function like this:
return () => h('div', [count.value, object.foo])
- As the first parameter, the props object is received, which can be monitored by watchEffect.
- Take the context object as the second argument. This object contains the attrs, slots, and emit attributes.
ref
Ref creates a wrapper object that has a single reactive property value. If the object is specified as ref’s value, the object will be deeply traversed by reactive methods. Value and assignment are required through x.value in js code, but the wrapper object is handled automatically in the template and x.value is not called separately.
Take the example of a counter.
<template> counter: <span>{{ count }}</span> <button @click="countPlus">{{ plusText }}</button> <button @click="countMinus">{{ minusText }}</button> </template> <script> import { setup, ref } from 'vue' export default { name: 'Counter', Setup () {const plusText = 'add' const minusText = 'reduce' const count = ref(0) const countPlus = () => { count.value++ } const countMinus = () => { count.value-- } return { plusText, minusText, count, countPlus, countMinus } } } </script>Copy the code
reactive
Reactive objects are created by Reactive. The created reactive objects are not wrapped objects and do not need to use x.value. Reactive is equivalent to vue.Observable of Vue 2.x, which fetches a responsive proxy object for an object
computed
The ability to evaluate properties in Vue 2.x is very powerful and will definitely remain in VUe3.0, but it is now a higher-order function with the composition Api.
watchEffect
This is done automatically once under Setup to collect dependencies and fires incoming functions when dependencies change, making it a good place to put “side effects” into callback functions, such as asynchronous requests with unknown results.
Combine the three points above and give an example.
<template> < button@click ="mulitple"> I was clicked {{state.count}} times </button> value is {{result}} </template> <script> import {setup, ref, reactive, computed, watchEffect } from 'vue' export default { name: 'Multiplier', setup() { const state = reactive({ count: 0}) const result = ref(1) const mulitple = () => {state.count++} Actually, it's better to use computed watchEffect(() => {result.value *= (state.count + 1)}) return {state, result, mulitple } } } </script>Copy the code
toRefs
ToRefs transforms reactive objects into normal objects, where each attribute on the resulting object is a ref reference object that points to the corresponding attribute in the original object. This is useful when composite functions return reactive state, ensuring that the response of the original reactive object is not lost when the object is deconstructed or extended by the operator.
Such as their own custom hook for reuse.
// usePosition.js
import { reactive, toRefs, onMounted, onUnmounted } from 'vue'
const usePosition = () = > {
const position = reactive({
x: 0.y: 0
})
const updatePosition = (e) = > {
position.x = e.pageX
position.y = e.pageY
}
onMounted(() = > {
window.addEventListener('mousemove', updatePosition)
})
onUnmounted(() = > {
window.removeEventListener('mousemove', updatePosition)
})
return position
}
export default usePosition
// MousePosition.vueX: {{x}},y: {{y}} </template><script>
import usePosition from '@/hooks/usePosition'
export default {
name: 'MousePosition'.setup() {
// Method 1: unresponsive
// const {x, y} = usePosition()
// return { x, y }
// Method two: unresponsive
// return { ... usePosition() }
// Method 3: The only way to remain responsive without using toRefs
return {
pos: usePosition()
}
}
}
</script>
Copy the code
If toRefs is used to solve this problem, methods 1 and 2 above can be used.
// usePosition.js
import { reactive, toRefs, onMounted, onUnmounted } from 'vue'
const usePosition = () = > {
// ...
return toRefs(position)
}
export default usePosition
Copy the code
Lifecycle hook functions
The second major change of VUe3.0 is the addition of lifecycle hook functions. The mapping between vue2.x and VUe2.x is shown in the following table:
vue2.x | vue3.0 | instructions |
---|---|---|
beforeCreate | setup | Before component creation |
created | setup | Component creation complete |
beforeMount | onBeforeMount | Components before mounting |
mounted | onMounted | Components are mounted successfully |
beforeUpdate | onBeforeUpdate | Data update before view update |
updated | onUpdated | Data update, view update render complete |
beforeDestroy | onBeforeUnmount | Before component destruction |
destroyed | onUnmounted | Component destruction completed |
As you can see from the table above, the new lifecycle hook functions are basically the same as the original, with slightly different destruction and creation phases. Adding the on prefix along with the setup function makes it more intuitive.
These new lifecycle hook functions are used as follows:
<template>
<button @click="add">{{ count }}</button>
</template>
<script>
import { setup, ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue'
export default {
name: 'LifeCycle'.beforeCreate() {
console.log('beforeCreate')},created() {
console.log('created')},setup() {
console.log('setup')
const count = ref(0)
const add = () = > {
count.value++
}
onBeforeMount(() = > {
console.log('beforeMounted')
})
onMounted(() = > {
console.log('onMounted')
})
onBeforeUpdate(() = > {
console.log('onBeforeUpdate')
})
onUpdated(() = > {
console.log('onUpdated')
})
onBeforeUnmount(() = > {
console.log('beforeMounted')
})
onUnmounted(() = > {
console.log('onUnmounted')})return {
count,
add
}
}
}
</script>
Copy the code
As you can see from the figure, the setup hook function is triggered before the beforeCreate, and the old lifecycle hook function is still available
Bidirectional binding V-Model
In vue3.0, we use the sync modifier to create multiple bidirectional bindings on a component. Vue3.0 adds the v-model: XXX modifier to the v-model syntax to implement the bidirectional binding of multiple attributes.
<! -- MyInput.vue -->
<template>The Numbers:<input type="number" :value="number" @input="numberInput" />Text:<input type="text" :value="text" @input="textInput" />
</template>
<script>
import { setup } from 'vue'
export default {
name: 'MyInput'.props: {
number: Number.text: String
},
setup(props, ctx) {
console.log(ctx)
const numberInput = e= > {
ctx.emit('update:number', e.target.value)
}
const textInput = e= > {
ctx.emit('update:text', e.target.value)
}
return {
numberInput,
textInput
}
}
}
</script>
<! -- MyForm.vue -->
<template>
<my-input v-model:number="number" v-model:text="text" />
<p>The Numbers: {{number}}</p>
<p>Text: {{text}}</p>
</template>
<script>
import MyInput from '@/components/MyInput.vue'
import { setup, ref } from 'vue'
export default {
name: 'MyForm'.components: {
MyInput
},
setup() {
const text = ref(' ')
const number = ref(0)
return {
text,
number
}
}
}
</script>
Copy the code
The effects are as follows:
vue-router
With all the examples above, let’s make it a one-page app and roll up our sleeves.
Create folders and files as follows:
Then install vue-Router
npm install vue-router@next -D
// or
yarn add vue-router@next -D
Copy the code
In the SRC /router/index.js file, write the following:
import {
createRouter,
createWebHistory,
createWebHashHistory
} from 'vue-router'
import Home from '@/views/Home.vue'
const routes = [
{
path: '/'.component: Home,
name: 'Home'.meta: {
title: 'home'}}, {path: '/counter'.component: () = > import('@/views/Counter.vue'),
name: 'counter'.meta: {
title: 'counter -ref API'}}, {path: '/mousePosition'.component: () = > import('@/views/MousePosition.vue'),
name: 'mousePosition'.meta: {
title: 'Mouse Coordinate - Reactive API'}}, {path: '/multiplier'.component: () = > import('@/views/Multiplier.vue'),
name: 'multiplier'.meta: {
title: 'Multiplier -watchEffect API'}}, {path: '/lifeCycle'.component: () = > import('@/views/LifeCycle.vue'),
name: 'lifeCycle'.meta: {
title: 'Life cycle'}}, {path: '/form'.component: () = > import('@/views/MyForm.vue'),
name: 'form'.meta: {
title: 'Multi-valued bidirectional binding V-model'}}]const router = createRouter({
// Route using hash mode, url marked with #
history: createWebHashHistory(),
// Routes in history mode, like normal urls, need background configuration
// history: createWebHistory(),
routes
})
export default router
Copy the code
Then we create a menu component, the logic is very simple, get the route written above, simple processing output into the menu.
<! -- src/components/Menu.vue -->
<template>
<ul>
<li v-for="menu in menus" :key="menu.path">
<router-link :to="menu.path">{{ menu.name }}</router-link>
</li>
</ul>
</template>
<script>
import router from '@/router'
import { setup, computed } from 'vue'
export default {
name: 'Menu'.setup() {
// Calculate attributes
const menus = computed(() = > {
// Get all routing information
const routes = router.getRoutes()
const res = []
routes.forEach(route= > {
res.push({
name: route.meta.title,
path: route.path
})
})
return res
})
return {
menus
}
}
}
</script>
Copy the code
Finally, boot it up again and experience the joy of Vue3.0.
conclusion
I had a preliminary experience with VUe3.0. In terms of style, I could obviously feel that it was close to React, but it could not get rid of the writing style of “object form”, giving people a strange feeling that it could not get up or down. However, after the first step, there will definitely be a better second step.