preface

Vue3 has been out for some time, and I have been learning, but the owner of the building is a student, and has been doing some wechat mini programs and visual things recently, so the update is a bit slow. Please understand.

This article is mainly about their own Vue3 + Ts + Vite learning process, their own understanding is to use the method

Ts

Personal Ts can not be said to learn very well, can only say that part of the foundation. So not to mislead you here, this article will be used briefly in Vue3 below.

If you want to learn more. The following books are recommended

I recommend a TypeScript primer for this book, which is available on Github(science may be required). (I recommend reading this book first, I feel it is easy to understand)

The second book is the official manual (or may require scientific Internet), compared to the first book I find it more difficult to understand, so I first choose the first book

Vu3 basic environment and syntax

Configure the VUE3 development environment

// Install or upgrade
npm install -g @vue/cli
yarn global add @vue/cli


// Update vueCli globally
npm update -g @vue/cli
/ / or
yarn global upgrade --latest @vue/cli



// Ensure that the Vue CLI version is later than 4.5.0
vue --version

// Create the project
vue create my-project

// Or use a graphical interface
vue ui
Copy the code

The CSS preprocessor is recommended

Dart-sass is the preferred option (much poisoned by Node-sass). This is also officially recommended

Steps on the command line. Mainly some configuration, we can refer to some of their own use. I recommend Vue UI creation. Probably pretty

The change of the template

In Vue2, each template node can have only one root node.

In Vue3, you can have multiple root nodes

For example

//vue2
<template>
    <div>
        
    </div>
</template>


// vue3
<template>
  <div>
    
  </div>
  <router-view/>
</template>
Copy the code

New grammar

setUp

This replaces the old data function, which accepts two parameters, props and context. This command is executed only once

The sample code

<template> <div> <h1> num:{{num}} </h1> </div> < button@click ="add"> add 1</button> </template> <script lang="ts"> export default { name: 'App', setup () { let num = 0 const add = () => { num++ } console.log(num) return { add, num } } } </script>Copy the code

This is a basic setup process that you may not be able to see here. Try clicking on the button and see that num has not changed. If you look at the console (F12), you see that the output is a value of number. But our clicks didn’t change. So next, we’re going to use our first new API Ref

Ref

First, let’s modify the code above

< the template > < div > < h1 > MSG: {{MSG}} < / h1 > < h1 > num: {{num}} < / h1 > < / div > < button @ click = "add" > 1 < / button > < / template > <script lang="ts"> import { ref } from 'vue' export default { name: 'App', setup () { const msg = ref(0) let num = 0 const add = () => { msg.value++ num++ } console.log(msg) console.log(num) return { msg, add, num } } } </script>Copy the code

First of all, let’s look at the reference form, and you can see that this is an on-demand import, and I’m not going to go through all the benefits, but let’s go straight to the console

One is RefImpl and one is the value 0. MSG can be changed, but num cannot.

What is a RefImpl? I understand it as a proxy object. For example, we know that data in Vue2 is intercepted via Object.defineProperty(). So as to achieve the purpose of data response type. Vue3 makes use of proxies in ES6 and has more ways to intercept than Object.defineProperty(). It’s also more powerful.

So, do you know why MSG can change and num can’t change? Because MSG is being proxied by proxy. Num did not. That’s why we’re in this situation

Explain the Ref

Ref is a function that takes an argument and returns a responsive object.

In this example, the 0 we initialized is wrapped around the object as a parameter, and changes can be detected and made when the value is manipulated in the future.

What if I wanted to create an object? At this point, you can’t use this function in time. And you’re going to use this function Reactive

Reactive

To create a reactive state for a JavaScript object, use the reactive method:

The sample code

<template>
  <div>
    {{ data }}
    <h1>
      count:{{ data.count }}
    </h1>
    <h1>
      double:{{ data.double }}
    </h1>
  </div>
  <button @click="data.increase">Add 1</button>
</template>

<script lang="ts">
import { reactive, computed } from 'vue'

export default {
  name: 'App',
  setup () {
    const data = reactive({
      count: 0.increase: () = > {
        data.count++
      },
      double: computed(() = > data.count * 2)})return {
      data
    }
  }
}
</script>
Copy the code

At this point, we can click the button to change the state.

Computed attributes, much the same for Vue2. Those of you who have studied Vue2 can understand. If you do not understand, please refer to the follow-up special introduction

{{count}} {count}} {count}} {count}} {count}} {count}} Deconstruction comes to mind in Es6

So the code is changed to the following

<template> <div> <h1> count:{{ count }} </h1> <h1> double:{{ double }} </h1> </div> <button @click="increase"> </button> </template> <script lang="ts"> import {reactive, computed } from 'vue' export default { name: 'App', setup () { const data = reactive({ count: 0, increase: () => { data.count++ }, double: computed(() => data.count * 2) }) return { ... data } } } </script>Copy the code

This is where you see a problem. Why doesn’t clicking on a button change the state?

This is because deconstruction breaks the proxy by programming it to a normal value. As in the Ref example above, clicking the button does not change.

At this point, it’s time to bring up another new API, toRefs

toRefs

This is a quote from the official website

This function is useful when toRefs returns a reactive object from a composite function, so that components can deconstruct/extend the returned object without losing its reactivity:

It’s easy to use, just add it when you return

<template> <div> <h1> count:{{ count }} </h1> <h1> double:{{ double }} </h1> </div> <button @click="increase"> add 1</button> </template> <script lang="ts"> import {reactive, computed, toRefs } from 'vue' export default { name: 'App', setup () { const data = reactive({ count: 0, increase: () => { data.count++ }, double: computed(() => data.count * 2) }) return { ... toRefs(data) } } } </script>Copy the code

Life cycle change

beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted
errorCaptured onErrorCaptured
renderTracked onRenderTracked
renderTriggered onRenderTriggered

Tips (from the official website)

Because setup runs around beforeCreate and Created lifecycle hooks, you don’t need to explicitly define them. In other words, any code that will be written in these hooks should be written directly in the Setup function.

Originally the grammar

watch

// Watch basic use remember to introduce
watch(data, () = > {
  document.title = 'After update' + data.count
})
// Two arguments to watch, representing the new value and the old value
watch(data, (newValue, oldValue) = > {
  console.log('old', oldValue)
  console.log('new', newValue)
  document.title = 'After update' + data.count
})

// watch returns an array of values
watch([greetings, data], (newValue, oldValue) = > {
  console.log('old', oldValue)
  console.log('new', newValue)
  document.title = 'After update' + greetings.value + data.count
})

// Use the function getter
watch([greetings, () = > data.count], (newValue, oldValue) = > {
  console.log('old', oldValue)
  console.log('new', newValue)
  document.title = 'After update' + greetings.value + data.count
})
Copy the code

Note in particular that data is an object. I’m going to take the inside value

computed

Use the same method as vue2. It can be written inside or outside reactive

setup () {
    const data = reactive({
      count: 0.increase: () = > {
        data.count++
      },
      double: computed(() = > data.count * 2)})const conComputed = computed(() = > data.count * 2)
    const number = ref(0)
    watch(data, () = > {
      console.log(data)
      document.title = 'updated ' + data.count
    })
    watch(number, () = > {
      console.log(number)
    })
    return{ number, conComputed, ... toRefs(data) } }Copy the code

The new labels

Teleport

Teleport English document address

Usually our mask layer exists under some multilevel tag, which is actually not reasonable.

Teleport appears to move the component we wrote below the specified label.

To is what label to move to. Support selector

The sample code

// model
<template>
  <teleport to="#modal">
    <div id="center">
      <h1>this is a modal</h1>
    </div>
  </teleport>
</template>

<script>
export default {
  name: 'model'
}
</script>

<style scoped lang="scss">
#center {
  width: 200px;
  height: 200px;
  background: red;
  position: fixed;
  left: 50%;
  top: 50%;
  margin-left: -100px;
  margin-top: -100px;
}
</style>


// App.vue
<template>
  <model v-if="show"></model>
  <button @click="show = ! show">show</button>
</template>

<script lang="ts">
import { ref } from 'vue'
import model from './components/model.vue'
export default {
  name: 'App'.components: {
    model
  },
  setup () {
    const show = ref(false)
    return {
      show,
    }
  }
}
</script>
Copy the code

After we click the button

That’s the basic usage.

Suspense Asynchronous Request

This new TAB is the one I find most useful. Fixed white space in our asynchronous request when images were returned too slowly (although this can be fixed. But not as convenient.)

The sample code

// showPic
<template>
  <img :src="result && result.url" alt="">
</template>

<script lang="ts">
import axios from 'axios'
import { defineComponent } from 'vue'
export default defineComponent({
  async setup () {
    const rawData = await axios.get('https://picsum.photos/id/786/200/200')
    console.log(rawData)
    return {
      result: rawData.config
    }
  }
})
</script>
// App
<template>
  <Suspense>
    <template #default>
      <pic-show />
    </template>
    <template #fallback>
      <h1>Loading ! .</h1>
    </template>
  </Suspense>
</template>

<script lang="ts">
import ShowPic from './components/ShowPic.vue'
export default {
  name: 'App'.components: {
    ShowPic
  }
}
</script>
Copy the code

Results the following

Custom HOOKS

This section shows just one example of how to extract common logic code. Just to show you how to pull away.

//useMousePosition.ts
import {onMounted, onUnmounted, ref} from "vue";

function useMousePosition() {
    const x = ref(0)
    const y = ref(0)
    const updateMouse = (e: MouseEvent) = > {
        x.value = e.pageX
        y.value = e.pageY
    }

    onMounted(() = > {
        document.addEventListener('mousemove', updateMouse)
    })

    onUnmounted(() = > {
        document.removeEventListener('mousemove', updateMouse)
    })
    return {x, y }
}

export default useMousePosition

// app
import useMousePosition from "@/hooks/useMousePosition";
export default {
    setup(){
        const {x, y} = useMousePosition()
        return {
            x,y
        }
    }
}
Copy the code

Develop components with Ts

New grammar

DefineComponent. This package is needed to create the component.

PropType type assertion determines which type (already exists in Vue2)

/ * * * / defined
// Let's write the TS template and style
<script lang="ts">
import {
  defineComponent,
  PropType,
  computed
} from 'vue'

// Export interface types. Import the interface and define the interface during use
export interface ColumnProps {
  id: number; title: string; avatar? : string; des: string; }export default defineComponent({
  name: 'ColumnList'.props: {
    list: {
      type: Array as PropType<ColumnProps[]>,
      required: true}},setup(props) { // props is used here
    const ColumnList = computed(() = > {
      return props.list.map((item) = > {
        if(! item.avatar) { item.avatar =require('@/assets/logo.png')}return item
      })
    })
    return {
      ColumnList
    }
  }
})
</script>


/ / use
<template>
  <div id="container">
    <ColumnList :list="list"></ColumnList>
  </div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ColumnList, { ColumnProps } from '@/components/ColumnList.vue'

const testDate: ColumnProps[] = [
  {
    id: 1.title: 'test1'.des: 'test1'.avatar: 'https://picsum.photos/id/239/200/200'
  }, {
    id: 2.title: 'test1'.des: 'test1'.avatar: 'https://picsum.photos/id/239/200/200'
  }, {
    id: 3.title: 'test1'.des: 'test1'.avatar: 'https://picsum.photos/id/239/200/200'
  }, {
    id: 4.title: 'test1'.des: 'test1'.avatar: 'https://picsum.photos/id/239/200/200'}]export default defineComponent({
  name: 'App'.components: {
    ColumnList,
  },
  setup() {
    return {
      list: testDate,
    }
  }
})
</script>
Copy the code

The above is some of my practical experience in the process of learning and using. If there is any wrong, you are welcome to criticize

Vite

What is a Vite

github:github.com/vitejs/vite

Vite is a native ESM-driven Web development build tool. Developed based on browser native ES Imports in development environment and packaged based on Rollup in production environment.

Vite is a browser native ES Modules development server. Using the browser to parse the module, compiling and returning it on demand on the server side, bypassing the concept of packaging completely and allowing the server to be used on demand. Not only does it have Vue file support, it also handles hot updates, and the speed of hot updates does not slow down with the increase of modules.

It mainly has the following characteristics:

  • Quick cold start
  • Instant module hot update
  • True on-demand compilation

Simple installation and use

// npm i vite

npm init vite-app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev
Copy the code

File directory after installation

Open the page as

As for integrating vue-Router, etc. Please feel for yourself. Here is just a brief introduction

The principle of

The implementation of Vite is a modular feature based on native browser support

When script.type is module, files imported by SRC and import will send HTTP requests.

As can be seen from the figure, a request is sent to obtain the file, which will be requested only when needed. True load on demand

Simple implementation

First step, initial setup

First, I will build a basic framework and create a vitetext.js file in the root directory. Implementation based on Koa. If you don’t know. Please visit the Koa website

const fs = require('fs')
const path = require('path')
const Koa = require('koa')


const app = new Koa()

// This is mainly used for redirecting requests
function rewriteImport(content) {
// The purpose is to modify the contents of the.js file, not /./.. / import, replaced with /@modules/
  return content.replace(/ from ['|"]([^'"]+)['|"]/g.function(s0,s1){
    // console.log(s0,s1)
    if(s1[0]! = ='. '&&s1[1]! = ='/') {return ` from '/@modules/${s1}'`
    }else{
      return s0
    }
  })
}

// This is a convenient way to use middleware directly.
app.use((ctx, next) = >{
  const { request: { url } } = ctx
  if (url === '/') { // Find whether to return index file when requesting root directory
    let content = fs.readFileSync('./index.html'.'utf-8')
    ctx.type = 'text/html'
    ctx.body = content
  }
  next()
})


app.listen(8888.() = >{
  console.log('http://localhost:8888/')})Copy the code

Next we start the service, the access path.

  1. We can see that. We successfully requested localhost

  2. Index. HTML

    Failed to send the request.

The second part integrates JS

Remember to write the next () method or the next middleware will not be executed
app.use((ctx, next) = >{
  const { request: { url } } = ctx
  if (url.endsWith('.js')) {
    console.log(url.slice(1)) // Prints out the request path
    const p = path.resolve(__dirname, url.slice(1)) // Find the corresponding file
    const content = fs.readFileSync(p, 'utf-8') // Read the file
    ctx.type = 'application/javascript'
    ctx.body = rewriteImport(content) // Replace some with paths
  }
  next()
})
Copy the code

So we can see that

  1. But there are some red ones. There was no success in getting a response. That ismain.jsSome of them are likeimport { createApp } from 'vue'These. We didn’t find it. So the next step is to make these accessible

Step 3 Change the modules path

As you can see, the request path for Vue is http://localhost:8888/@modules/ Vue. But we don’t have this **@** path in the folder. So we need to do a special treatment for this.

app.use((ctx, next) = >{
  const { request: { url } } = ctx
  if (url.startsWith('/@modules/')) {
    const prefix = path.resolve(__dirname, 'node_modules', url.replace('/@modules/'.' '))
    console.log(prefix)  // Print the concatenated path
    const module = require(prefix + '/package.json').module
    console.log(module) // Get the path to vue/module
    const p = path.resolve(prefix, module)
    console.log(p) // Splice read again
    const ret = fs.readFileSync(p, 'utf-8')
    ctx.type = 'application/javascript'
    ctx.body = rewriteImport(ret)
  }
  next()
})
Copy the code

The main thing to notice. It’s a file path read plus splicing. It can be a little confusing. Large print several times. Just know each place

At this time. We can request Vue

Step 4 parses the Vue file and returns

  • Here you need to use the two modules provided officially

    1. @vue/compiler-sfc: the officialvueSingle-file parser
    2. @vue/compiler-dom: afterParser, Transform, generateThat will beVirtual domRender to the browserReal dom
    app.use((ctx, next) = >{
      const { request: { url,query } } = ctx
      if (url.indexOf('.vue') > -1) {
        // import xx from 'xx.vue'
        // 1. Single-file component parsing
        console.log(123456)
        const p = path.resolve(__dirname, url.split('? ') [0].slice(1))
        // We need an official library
        const { descriptor } = compilerSfc.parse(fs.readFileSync(p, 'utf-8'))
        if(! query.type) {/ / js content
          ctx.type = 'application/javascript'
          ctx.body = `
    ${ rewriteImport(
              descriptor.script.content.replace('export default '.'const __script = '))}
    import {render as __render} from "${ url }? type=template" __script.render = __render export default __script `
        } else if (query.type == 'template') {
          // Parse our template programming render function
          const template = descriptor.template
          const render = compilerDom.compile(template.content, { mode: 'module' }).code
          ctx.type = 'application/javascript'
          ctx.body = rewriteImport(render)
        }
      }
      next()
    })
    Copy the code

That’s when you see it. App.vue is already recognized and returned

But. It didn’t work. Why is that?

After some Baidu. A process object is missing. This is only available in Node ·. Not on the browser side. So. So let’s rewrite the first step

app.use((ctx, next) = >{
  const { request: { url } } = ctx
  if (url === '/') {
    let content = fs.readFileSync('./index.html'.'utf-8')
    content = content.replace('<script'.`  )
    ctx.type = 'text/html'
    ctx.body = content
  }
  next()
})
Copy the code

Like this. We gave the browser a process property. You can identify it

Last step. Parsing the CSS

Similar to parsing JS

app.use((ctx, next) = >{
  const { request: { url } } = ctx
  if (url.endsWith('.css')) {
    const p = path.resolve(__dirname, url.slice(1))
    const file = fs.readFileSync(p, 'utf-8')
    const content = `
      const css = "${ file.replace(/\n/g.' ')}"
      const link = document.createElement('style')
      link.setAttribute('type', 'text/css')
      document.head.appendChild(link)
      link.innerHTML = css
      export default css
    `
    ctx.type = 'application/javascript'
    ctx.body = content
  }
  next()
})
Copy the code

And then we identified all the files

conclusion

If you want to add other files. All you need to do is follow this method and do the corresponding way to load

The problem found is not resolved

  1. When we’re done with the initial setup. First parse the CSS. CSS does not work, but the introduction of Vue’s two official libraries does.

    If there’s anything I can do to help. Thank you for

conclusion

This article mainly summarizes.

  1. New syntax and usage for VU3
  2. Some small examples of Vue + TS development
  3. Vite understanding and simple imitation