Project introduction

Vue3-jd-h5 is an e-commerce H5 page front-end project, based on Vue 3.0.0-beta.1 + Vant implementation, mainly including home page, classification page, my page, shopping cart, etc.

πŸ“– local offline code Vue2.6 was developed using MockJS data in the branch demo, and the renderings can be seen here at πŸ”—

⚠️master branch is the code of the online production environment, because part of the background interface has been suspended 😫, may not be able to see the actual effect.

πŸ“Œ There are still many deficiencies in this project. If you want to contribute to this project, you are also welcome to give us PR or issue.

πŸ”‘ This project is free and open source. If you have partners who want to carry out secondary development on the basis of secondary development, you can clone or fork the whole warehouse. I will be very glad if you can help. πŸ™

Vue3 Construction procedure

  1. First, select a file locally and clone the code locally:
git clone https://github.com/GitHubGanKai/vue-jd-h5.git 
Copy the code
  1. View all branches:
gankaideMacBook-Pro:vue-jd-h5 gankai$ git branch -a
  demo
  vue-composition-api
  dev
  feature
  gh-pages
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/demo
  remotes/origin/vue-composition-api
  remotes/origin/dev
  remotes/origin/feature
  remotes/origin/gh-pages
  remotes/origin/master
Copy the code
  1. Switch to the vue-comaction-API branch and start developing!

  2. Run the NPM install command in the IDEA command line to download related dependencies.

  3. πŸ”§ The development environment runs the NPM run dev command in the IDEA command line to run the project.

  4. πŸ“¦ in the IDEA command line run command: NPM run DLL :build, package the project,πŸ“± mobile phone scan the following πŸ‘‡ TWO-DIMENSIONAL code to view!

Initialization of the project

πŸ’‘ If you are slow to install the package, it is because the NPM server is in a foreign country, here is a recommended tool for you to switch the NPM image at any time πŸ‘‰NRM, sometimes, when we develop, in order to speed up the installation of the package, we need to switch the image source to the domestic, but, If we need to publish some of our own components to NPM, we have to switch back and forth, with this much easier! Use $NPM install -g NRM to install globally. Then, you can use NRM ls to view all images:

gankaideMacBook-Pro:~ gankai$ nrm ls

  npm -------- https://registry.npmjs.org/
* yarn ------- https://registry.yarnpkg.com/
  cnpm ------- http://r.cnpmjs.org/
  taobao ----- https://registry.npm.taobao.org/
  nj --------- https://registry.nodejitsu.com/
  npmMirror -- https://skimdb.npmjs.com/registry/
  edunpm ----- http://registry.enpmjs.org/
Copy the code

If you need to use Taobao image, execute: NRM use Taobao can switch the source at any time, of course, there is also a NPM package version management tool NVM, mainly to manage the package version, if you are interested in small friends, you can go to know, here is not tedious 😊!

The installation

Go to the clone project root directory and install @vue/composition-api to experience vue3’s new features.

NPM install:

npm install @vue/composition-api --save
Copy the code

Yarn installation:

yarn add @vue/composition-api
Copy the code

CDN

<script src="https://unpkg.com/@vue/composition-api/dist/vue-composition-api.umd.js"></script>
Copy the code

Use the global variable window.vuecompositionAPI.

use

Before you can use any of the capabilities provided by @vue/composition-api, you must install them via vue.use () :

In the entry file main.js:

import Vue from 'vue';
import VueCompositionApi from '@vue/composition-api';

Vue.use(VueCompositionApi);
Copy the code

After installing the plug-in, you can use the new Composition API to develop components.

⚠️ Currently vue official for VUe-CLI provides a plug-in vue-cli-plugin-vue-next, you can also directly add the latest version in the project!

# in an existing Vue CLI project
vue add vue-next
Copy the code

If you want to experience the new version from scratch, you can use this method to install it. Because our project relies on third-party libraries, if it is installed globally, the third-party UI library of the whole project cannot run! So we chose to install @vue/ composition-API to try it out and slowly transition to the latest version of VUe3

Vue 3.0 Composition-API Basic feature experience

The setup function

The setup() function is a new component property in VUe3, and is the equivalent of the 2.x created function, which handles all of the component logic options in previous versions. It provides a unified entry point for using vue3’s new Composition API feature. Setup functions are executed after beforeCreate and before created, relative to 2.x! For details, please refer to:

vue2.x vue3
beforeCreate The setup (alternative)
created The setup (alternative)
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeDestroy onBeforeUnmount
destroyed onUnmounted
errorCaptured onErrorCaptured

A new hook

In addition to the 2.x lifecycle equivalents, the Composition API provides the following Debug hooks:

  • onRenderTracked
  • onRenderTriggered

Both hooks receive DebuggerEvent options similar to onTrack and onTrigger observers:

export default {
  onRenderTriggered(e) {
    debugger
    // inspect which dependency is causing the component to re-render}}Copy the code

Dependency injection

Enable dependency injection similar to the 2.x provide/inject option. Both can only be called during setup(), the current active instance.

import { provide, inject } from '@vue/composition-api'

const ThemeSymbol = Symbol(a)const Ancestor = {
  setup() {
    provide(ThemeSymbol, 'dark')}}const Descendent = {
  setup() {
    const theme = inject(ThemeSymbol, 'light' /* optional default value */)
    return {
      theme
    }
  }
}
Copy the code

Inject accepts an optional default value as the second parameter. Inject returns undefined if a default value is not provided and the attribute is not found in the Provide context.

Inject reactive data

To maintain a response between the supplied value and the injected value, use ref

// In parent form
const themeRef = ref('dark')
provide(ThemeSymbol, themeRef)

/ / components
const theme = inject(ThemeSymbol, ref('light'))
watchEffect(() = > {
  console.log(`theme set to: ${theme.value}`)})Copy the code
  1. becausesetupThe function takes two parameters, the first of which isinitProps, the value sent by the parent component! , the second parameter is aContext object

SetupContext, which has the following main properties:

attrs: Object    $attrs = this.$attrs in vue 2.x
emit: Ζ’ ()       / / equivalent enclosing $emit ()
isServer: false   // Whether it is server rendering
listeners: Object   // Identical to this.$listeners in vue2. X
parent: VueComponent  // Equivalent to this.$parent in vue2
refs: Object  // equivalent to this.$refs in vue2.x
root: Vue  // Root is the globally unique instance object returned when newVue() is used in main.js. Note that this is not confused with the single file component
slots: {}   // Same as this.$slots in vue2. X
ssrContext: {}// Server render related
Copy the code

⚠️ Note: This is not accessible in the setup() function, whether this refers to a global vue object (i.e., the global vue instance object generated using new in main.js) or a single-file object.

But what if we want to access the instance object of the current component? We can introduce the getCurrentInstance API, and the return value is the instance of the current component!

import { computed, getCurrentInstance } from "@vue/composition-api";
export default {
  name: "svg-icon".props: {
    iconClass: {
      type: String.required: true
    },
    className: {
      type: String}},setup(initProps,setupContext) { 
    // ⚠️ note that this place needs to be structed out of CTX if added via vue add vue-next
    const ctx = getCurrentInstance();
    const iconName = computed(() = > {
      return `#icon-${initProps.iconClass}`;
    });
    const svgClass = computed(() = > {
      if (initProps.className) {
        return "svg-icon " + initProps.className;
      } else {
        return "svg-icon"; }});return{ iconName, svgClass }; }}; </script>Copy the code

Ref automatic unwrap

Ref () is used to create a responsive data object based on the given value. The return value of the ref() call is a wrapped object (RefImpl) with only a.value property on it. If we want to access the value of the object in the setup function, Value can be obtained from. Value, but if you are in the

import { ref } from '@vue/composition-api'

setup() {
    const active = ref("");
    const timeData = ref(36000000);
    console.log('output = = = >',timeData.value)
    return {
       active,
       timeData
    }
}
Copy the code
<template>
  <p>Active status: {{active}}</p>
  <p>Active time: {{timeData}}</p>
</template>
Copy the code

⚠️ Note: Do not place arrays in refs, Array index attributes cannot be automatically expanded, and do not use Array direct access to ref objects:

const state = reactive({
  list: [ref(0)]});// does not expand automatically, '.value 'must be used
state.list[0].value === 0; // true

state.list.push(ref(1));
// does not expand automatically, '.value 'must be used
state.list[1].value === 1; // true
Copy the code

When we need to manipulate the DOM, such as when we need to retrieve the DOM to use swiper in our project, we can also use πŸ‘‡!

  <div class="swiper-cls">
      <swiper :options="swiperOption" ref="mySwiper">
        <swiper-slide v-for="(img ,index) in tabImgs.value" :key="index">
          <img class="slide_img" @click="handleClick(img.linkUrl)" :src="img.imgUrl" />
        </swiper-slide>
      </swiper>
   </div>
Copy the code

Then define a const mySwiper = ref(null) in the setup function; In vue2.x, we can use this.$refs.myswiper to retrieve the DOM object. Now we can use the mySwiper function to return the same mySwiper as template.

import { ref, onMounted } from "@vue/composition-api";
setup(props, { attrs, slots, parent, root, emit, refs }) {
	const mySwiper = ref(null);
  onMounted(() = > {
    // Get the DOM object from myswiper. value!
    // You can also use refs.mySwiper in vue2.x, which is the same DOM object as myswiper. value.
    mySwiper.value.swiper.slideTo(3.1000.false);
  });
  return {
    mySwiper
  }
}
Copy the code

reactive

Reactive () receives a common object and returns a reactive data object, equivalent to vue.Observable () in vue 2.x. Vue 3.x provides reactive() to create a reactive data object Observer. In refs, we usually store data of primitive types, but we can use reactive functions if it is a reference type.

When reactive receives an Array, we can wrap an object around the Array and add a property such as Value (you can call the property whatever you like) to the object, and its value is the Array.

<script>
// The associated AIP must be imported before it can be used
import { ref, reactive } from "@vue/composition-api";
export default {
  name: "home".setup(props, { attrs, slots, parent, root, emit, refs }) {
    
    const active = ref("");
    const timeData = ref(36000000);
    // Make each object in the tabImgs array a responsive object
    const tabImgs = reactive({
      value: []});const ball = reactive({
      show: false.el: ""
    });
    return {
      active,
      timeData,
      tabImgs,
      ...toRefs(ball),
    };
  }
};
</script>
Copy the code

So in the template template when we want to access this array we need to use.value to get the value of that array.

<template>
    <div class="swiper-cls">
      <swiper :options="swiperOption" ref="mySwiper">
        <swiper-slide v-for="(img ,index) in tabImgs.value" :key="index">
          <img class="slide_img" @click="handleClick(img.linkUrl)" :src="img.imgUrl" />
        </swiper-slide>
      </swiper>
    </div>
</template>
Copy the code

isRef

IsRef () is used to determine whether a value is an object created by ref(). When you need to expand a value that might have been created for ref(), use isRef!

import { isRef } from '@vue/composition-api'

setup(){
  const headerActive = ref(false);
  // In the setup function, if the object is responsive, when accessing the property, be sure to add. Value!
  const unwrapped = isRef(headerActive) ? headerActive.value : headerActive
  return{}}Copy the code

toRefs

The toRefs function converts a reactive object to a normal object, where each attribute on the returned object is a ref pointing to the corresponding attribute in the original object. This can be very useful when converting all attributes on an object to reactive!

import { reactive,toRefs } from '@vue/composition-api'
setup(){
  // Ball is an Observer
  const ball = reactive({
    show: false.el: ""
  });
  // ballToRefs is a normal Object, but all properties in ballToRefs are RefImpl
  const ballToRefs  = toRefs(ball)
  // ref and the original attribute are "linked"
  ball.show = true
  console.log(ballToRefs.show) // true
  ballToRefs.show.value = false
  console.log(ballToRefs.show) // false
  return {
    ...ballToRefs    // By expanding the ballToRefs object, we can use all the attributes of the object like this directly in the template template!}}Copy the code

Click the Add button and the ball flies into the shopping cart animation:

<template>  
  <div class="ballWrap">
      <transition @before-enter="beforeEnter" @enter="enter" @afterEnter="afterEnter">
        <! Show -->
        <div class="ball" v-if="show">
          <li class="inner">
            <span class="cubeic-add" @click="addToCart($event,item)">
              <svg-icon class="add-icon" icon-class="add"></svg-icon>
            </span>
          </li>
        </div>
      </transition>
   </div>
</template>
Copy the code

computed

For the first parameter of a computed function, you can receive a function, or an object! If it is a function, it defaults to a getter function and returns a read-only ref object for the value returned by the getter.

import { computed } from '@vue/composition-api'

const count = ref(1)
// Computed receives a function as an input parameter
const plusOne = computed(() = > count.value + 1)

console.log(plusOne.value) / / 2

plusOne.value++ // Error, plusOne is read-only!
Copy the code

Or it can be an object, and you can use get and set objects to create writable ref objects.

const count = ref(1)
// Computed receives an object as an input parameter
const plusOne = computed({
  get: () = > count.value + 1.set: val= > {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) / / 0
Copy the code

watch

watch(source, cb, options?)

This watchAPI is exactly equivalent to 2.x this.$watch (and the corresponding watch option).

Looking at a single source

The observer data source can be a getter function that returns the value, or it can be a direct ref:

// Watching a getter
const state = reactive({ count: 0 })
watch(
  () = > state.count, // The getter function that returns the value
  (count, prevCount,onCleanup) = > {
    / *... * /})// directly watching a ref
const count = ref(0)
watch(
  count, // can also be directly ref
  (count, prevCount,onCleanup) = > {
  / *... * /
})
Copy the code

Watch multiple sources

An observer can also use arrays to monitor multiple sources simultaneously:

const me = reactive({ age: 24.name: 'gk' })
// Reactive
watch(
  [() = > me.age, () = > me.name], // Listen to reactive multiple data sources. You can pass in an array type and return a getter function
  ([age, name], [oldAge, oldName]) = > {
    console.log(age) // New age value
    console.log(name) // The new name value
    console.log(oldAge) // Old age value
    console.log(oldName) // The new name value
  },
  // options
  {
    lazy: true // By default, the watch callback is executed when the watch is created. If lazy is true, the watch callback is not executed.})setInterval(() = > {
  me.age++
  me.name = 'oldMe'
}, 7000000)

// ref type
const work = ref('web')
const addres = ref('sz')
watch(
  [work,address],  // Listen on multiple ref data sources
  ([work, addres], [oldwork, oldaddres]) = > {
   / /...
  },
  {
    lazy: true})Copy the code

The watch is bound to the life cycle of the component. When the component is uninstalled, the watch will stop automatically. In other cases, it returns a stop handle, which can be called to explicitly stop the observer:

// Watch returns a function handle, we can decide to stop and start the watch!
const stopWatch = watch(
  [work,address],  // Listen on multiple ref data sources
  ([work, addres], [oldwork, oldaddres]) = > {
   / /...
  },
  {
    lazy: true})// Call the stop function to clear monitoring of work and address
stopWatch()
Copy the code

Clear invalid asynchronous tasks in Watch

<div class="search-con">
  <svg-icon class="search-icon" icon-class="search"></svg-icon>
  <input v-focus placeholder="Search, keyword" v-model="searchText" />
</div>
Copy the code
setup(props, { attrs, slots, parent, root, emit, refs }){
  const CancelToken = root.$http.CancelToken 
  const source = CancelToken.source() 
  // Define reactive data searchText
  const searchText = ref(' ')

  // Send an asynchronous request to the background
  const getSearchResult = searchText= > {
   root.$http.post("http://test.happymmall.com/search", {text:searchText}, {
     cancelToken: source.token
   }).then(res= > {
    / /...
   });
  return source.cancel
}

// Define the watch listener
watch(
  searchText,
  (searchText, oldSearchText, onCleanup) = > {
    // Send the AXIos request and get the cancel function that cancels the AXIos request
    const cancel = getSearchResult(searchText)

    // If the watch listener is repeated, the last asynchronous request will be cleared first
    onCleanup(cancel)
  },
  // Watch is not executed when it is first created
  { lazy: true})return {
    searchText
  }
}
Copy the code

The last

Vue3 added Composition API. The new API is compatible with VUe2.x, and the @vue/ composition-API package can solve some of the problems we currently have with vue2.x. For example, how to organize logic, and how to extract and reuse logic across multiple components. We have some common logic reuse patterns based on Vue 2.x’s current API, but they all have more or less some problems:

These models include:

  1. Mixins
  2. Higher-order Components (aka HOCs)
  3. Renderless Components (Components based on Scoped slots/scope slots encapsulation logic)

In general, these modes have the following problems:

  1. The source of the data in the template is unclear. For example, when multiple mixins are used in a component, it can be difficult to tell from the template which mixin a property comes from. HOC has a similar problem.
  2. Namespace conflict. There is no guarantee that mixins developed by different developers won’t use exactly the same properties or method names. HOC has a similar problem with the injected props.
  3. Performance. Both HOC and Renderless Components require additional component instances to be nested to encapsulate logic, resulting in unnecessary performance overhead.

Vue3 added Composition API. And the new API is compatible with Vue2. X, we only need to introduce the @vue/ composition-API package into the project, which can solve most of the above problems. Also, if I had upgraded to vue3.x directly, I would have had a lot more work to do, as any third party UI library I was using in my current project would have had to be reworked and filled in a lot of holes! In the beginning, I just vue add vuE-next to the current scaffolding, but wherever there is a reliance on third-party ecologies, there are a lot of potholes…

Vue3. X does not export the default object export default. In the third-party environment, vue.xxx () is often used to rely on this object.

If it is a new team, small project, you can try to use VUe3 for trial development, slowly over, when vue3. X official release, and the surrounding ecology to follow, you can directly use VUe3!

In bilibili, Evan You also said that the most important thing for the current vue3 beta is to improve stability and support for third party libraries. If You are the author of the third party libraries, You can start to familiarize yourself with the source code now. We developers can first read all the API usage.

I’m sorry

⚠ ️ this project only change the current branch vue – composition – API under SRC/views/home/index. The vue and SRC/views/the classify index. The vue these two files, however, other changes are the same, If you are interested, you can fork to the page and write Composition API3. Get familiar with the new API, and I’ll work on the rest of the page as well! Everyone ⛽️ come on! When the official version is released, we can get started quickly! Hope to see you all again!

The end of the

❀️ Three things after reading: If you found this article inspiring, I’d like to ask you to do me a small favor:

  1. Like, so that more people can see this content, but also convenient to find their own content at any time (collection is not like, are playing rogue -_-);
  2. Pay attention to us, not regular points good article;
  3. Look at other articles as well;

πŸŽ‰ You are welcome to write your own learning experience in the comments section, and discuss with me and other students. Feel free to share this article with your friends if you find it rewarding.


This project has better experience effect on mobile terminal, you can scan the following TWO-DIMENSIONAL code to experience! πŸ‘‡

This article is formatted using MDNICE