Vue3.0 + Typescript first experience, create h5, webapp mobile

vue3-ts-template-h5

vue3

Vue3.0 + Typescript first experience, create h5, webapp mobile terminal template, out of the box

Foundation

  • Vue3 configuration
## 1\. Install vue-cli next
npm install --global @vue/cli@next

## 2\. When creating a project and creating a selection template, select "Manually select features", there are my options below, for reference only
vue create my-project-name

## If you already have a cli project is not TypeScript, cli can add a plug- 
vue add typescript

My Vue CLI Option

Vue CLI v4.5.4

  1. Please pick a preset: Manually select features
  2. Check the features needed for your project: Choose Vue version, Babel, TS, Router, Vuex, CSS Pre-processors, Linter
  3. Choose a version of Vue.js that you want to start the project with 3.x (Preview)
  4. Use class-style component syntax? Yes
  5. Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes
  6. Use history mode for router? (Requires proper server setup for index fallback in production) Yes
  7. Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with node-sass)
  8. Pick a linter / formatter config: Prettier
  9. Pick additional lint features: Lint on save
  10. Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys) In dedicated config files

New features and changes in vue3

1.v-model

2.x syntax

In 2.x, use the v-model equivalent binding on the assembly valueprop and inputevents:

<child-component v-model="title" />
<!-- 语法糖 默认mode prop:value  event:input-->
<child-component :value="title" @input="title = $event"/>

Using v-bind:sync vue is a one-way data flow, in order to “two-way binding” props, it can be implemented with sync

<child-component :title.sync="title" />
<!-- 语法糖 -->
<child-component :title="title" @update:title="title = $event"/>

In the child component, the parent component is notified by the following method

this.$emit('update:title',value)

3.x syntax

In 3.x, the custom component v-modelis passed modelValueprop and accepts the thrown update:modelValueevent, which is very similar to sync

<child-component v-model="title" />
<!-- 语法糖 -->
<child-component :modelValue="title" @update:modelValue="title = $event"/>

v-modelIf you need to change the parameter modelname, rather than a change in the assembly modeloption, but a argumentpass tomodel

<child-component v-model:title="pageTitle" />
<!-- 简写: -->
<child-component :title="title" @update:title="title = $event" />

Therefore, we can directly change the 2.x sync to the current writing

<child-component :title.sync="title" />
<!-- 替换为 -->
<child-component v-model:title="title" />

And we can write multiple v-models for one subcomponent

<child-component v-model:title="pageTitle" v-model:content="content"/>

v-for

The change of v-for is mainly reflected in the keyabove. When we use <template v-for>it, the key value of the 2.x syntax cannot be added to the templatelabel, but must be added to the child node, and in 3.x, it can be added template. And no need to add on the child nodekey

<!-- Vue 2.x -->
<template v-for="item in list">
  <div :key="item.id">...</div>
   ...
</template>

<!-- Vue 3.x -->
<template v-for="item in list" :key="item.id">
  <div>...</div>
  <span>...</span>
</template>

ref

In 2.0 v-for, we bind ref in, and we $refget a ref array. In 3.x, the array will not be created automatically. We need to bind a function, process it and accept it ourselves

<div v-for="item in list" :ref="setItemRef"></div>
export  default  { 
  setup ( )  { 
    //itemRefs does not have to be an array: it can also be an object, and its ref will be set by the iterated key. 
    let  itemRefs  =  [ ] 
    const  setItemRef  =  el  =>  { 
      itemRefs . push ( el ) 
    } 
    return  { 
      itemRefs ,
      setItemRef
    }
  }
}

Finishing… (coming soon😄)

CompositionApi

VUE 3 COMPOSITION API CHEAT SHEET

<template>
  <div>
    <p>Spaces Left: {{ spacesLeft }} out of {{ capacity }}</p>
    <h2>Attending</h2>
    <ul>
      <li v-for="(name, index) in attending" :key="index">
        {{ name }}
      </li>
    </ul>
    <button @click="increaseCapacity()">Increase Capacity</button>
  </div>
</template> <script>
// If using Vue 2 with Composition API plugin configured/ 在Vue2中使用 Composition API :  import { ref, computed } from "@vue/composition-api";
import { ref, computed } from "vue";
export default {
  setup() {
    //Data responsive package data in objects to track changes 
    const  capacity  =  ref ( 4 ) ; 
    const  attending  =  ref ( [ "Tim" ,  "Bob" ,  "Joe" ] ) ; 
    //Computed property 
    const  spacesLeft  =  computed ( ( )  =>  { 
      //Access the value of the responsive reference by calling .value 
      return  capacity . Value  -  attending . Value . Length ; 
    } ) ; 
    // define method 
    function increaseCapacity ( )  { 
      //If you need to modify the variable if you want to modify the variable for the ref response type, you need to operate it 
      . value capacity . value ++ ; 
    } 
    // Make our template can access these objects and functions 
    return  { capacity , attending , spacesLeft , increaseCapacity  } ; 
  } 
} ; 
</ script >

You can also write like this

import  {  reactive ,  computed ,  toRefs  }  from  "vue" ; 
export  default  { 
  setup ( )  { 
    //reactive accepts an object and returns a 
    reactive object const  event  =  reactive ( { 
      capacity :  4 , 
      attending :  [ "Tim" ,  " Bob" ,  "Joe" ] , 
      spacesLeft :  computed ( ( )  =>  {  return  event .capacity  -  event . attending . length ;  } ) 
    } ) ; 
    function  increaseCapacity ( )  { 
      // The reactive object returned by reactive does not need to use the value operation 
      event . capacity ++ ; 
    } 
    //...toRefs Deconstructs the object in the event , So that capacity or attending can be used directly in the template, without event.attending 
    return  {  ... toRefs ( event ) ,  increaseCapacity  } ; 
  } 
} ;

Finishing… (coming soon😄)

other

getCurrentInstance

In setup, there is no way to get vue through this, we can get vue instance through getCurrentInstance

Vant configuration

  • installation
## By npm install
npm i vant@next -S

## By installing yarn 
yarn add vant @ next
  • Use ts-import-plugin to implement vant on-demand introduction. If you cannot find these two packages locally, install them separately
//vue.config.js

// eslint-disable-next-line @typescript-eslint/no-var-requires
const merge = require("webpack-merge");
// eslint-disable-next-line @typescript-eslint/no-var-requires
const tsImportPluginFactory = require("ts-import-plugin");

module.exports = {
  chainWebpack: config => {
    config.module
      .rule("ts")
      .use("ts-loader")
      .tap(options => {
        options = merge(options, {
          transpileOnly: true,
          getCustomTransformers: () => ({
            before: [
              tsImportPluginFactory({
                libraryName: "vant",
                libraryDirectory: "es",
                style: true
              })
            ]
          }),
          compilerOptions: {
            module: "es2015"
          }
        });
        return options;
      });
  }
};
  • Mobile end adaptation (vw/vh scheme)
## Installation depends 
npm install postcss-px-to-
// vue.config.js
const pxtoviewport = require("postcss-px-to-viewport");

const autoprefixer = require("autoprefixer");

module . exports  =  { 
  css : { 
    loaderOptions : { 
      postcss : { 
        plugins : [ 
          autoprefixer ( ) , 
          pxtoviewport ( { 
            viewportWidth : 375 ,  // The width of the window corresponds to the width of our design draft, generally 750 
            minPixelValue : 1 ,  // Less than or equal to `1px` will not be converted to window units, you can also set to the value you want 
            unitPrecision : 3 ,  // Specify the number of decimal places for `px` to be converted to window unit values ​​(not evenly divisible in many cases) 
          } ) 
        ] 
      } 
    } 
  } 
}

Re-run, px changes to vw, ok✌~~

  • Registered vant global components, unified management, avoiding repeated references
// plugins/vant.ts

import { App as VM } from "vue";
import { Button, List, Cell, Tabbar, TabbarItem } from "vant";

const  plugins  =  [ Button ,  List ,  Cell ,  Tabbar ,  TabbarItem ] ;

export const vantPlugins = {
  install: function(vm: VM) {
    plugins.forEach(item => {
      vm.component(item.name, item);
    });
  }
};
//main.ts use

import { createApp } from 'vue'
import { vantPlugins } from './plugins/vant'

createApp ( the App ) 
  . . . // other configurations 
  . use ( vantPlugins ) 
  . Mount ( '#app' )

vant theme color

//详见/src/theme/var.less
// Color Palette
@black: #000;
@white: #fff;
@gray-1: #f7f8fa;
@gray-2: #f2f3f5;
@gray-3: #ebedf0;
@gray-4: #dcdee0;
@gray-5: #c8c9cc;
@gray-6: #969799;
@gray-7: #646566;
@gray-8: #323233;
@red: #ee0a24;
@blue: #1989fa;
@orange: #ff976a;
@orange-dark: #ed6a0c;
@orange-light: #fffbe8;
@green: #07c160;
@green1:#4fc08d;
// Gradient Colors
@gradient-red: linear-gradient(to right, #ff6034, #ee0a24);
@gradient-orange: linear-gradient(to right, #ffd01e, ## ff8917 );

// Component C
  • 1. Import the style file Add the above files and import them. Since the above vant configuration has been introduced, we need to adjust the path of the specified style
//vue.config.js
module.exports = {
   chainWebpack: config => {
    config.module
      .rule("ts")
      .use("ts-loader")
      .tap(options => {
        options = merge(options, {
          transpileOnly: true,
          getCustomTransformers: () => ({
            before: [
              tsImportPluginFactory({
                libraryName: "vant",
                libraryDirectory: "es",
                // --> 指定样式的路径
                style: name => `${name}/style/less`
              })
            ]
          }),
          compilerOptions: {
            module: "es2015"
          }
        });
        return options;
      });
  }
};
  • 2. Modify style variables
//vue.config.js
module.exports = {
  ...
  css : { 
    loaderOptions : { 
      //Configure less theme 
      less : { 
        lessOptions : { 
          modifyVars : { 
            // Directly overwrite the variable 
            "text-color" : "#111" , 
            "border-color" : "#eee" , 
            // or It can be overwritten by less file (the file path is absolute) 
            hack : `true; @import "./src/theme/var.less";` 
          } 
        } 
      } , 
    } 
  } 
}

Browser style reset

Reset the style sheet of the browser tabs. Because there are many types of browsers, the default styles of each browser are also different. For example, the button tabs have different styles in IE, Firefox, and Safari. So, by resetting the CSS properties of the button label, and then defining it uniformly, the same display effect can be produced. Before starting a project, create a reset.css, which can avoid many browser differences

/* http://meyerweb.com/eric/tools/css/reset/
   v5.0.1 | 20191019
   License: none (public domain)
*/

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, menu, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
main, menu, nav, output, ruby, section, summary,
time, mark, audio, video {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: 100%;
	font: inherit;
	vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, main, menu, nav, section {
	display: block;
}
/* HTML5 hidden-attribute fix for newer browsers */
*[hidden] {
    display: none;
}
body {
	line-height: 1;
}
menu, ol, ul {
	list-style: none;
}
blockquote, q {
	quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
	content: '';
	content: none;
}
table {
	border-collapse: collapse;
	border-spacing: 0;
}

1px border on mobile

  • Problem analysis: Some mobile phones have higher screen resolution, 2-3 times the screen. In the css style, the border: 1px solid red; under the 2x screen, it is not 1 physical pixel, but 2 physical pixels. The solution is as follows:
  • Use the pseudo-element of CSS::after + transfrom for scaling. Why use pseudo-element? Because the pseudo-element ::after or ::before is independent of the current element, it can be scaled separately without affecting the scaling of the element itself

Pseudo-elements can also be used by default in most browsers. They are in the same form as pseudo-classes, and the compatibility of single-quotes (ie) is better. I use scss to write mixins. Others are similar to compilers.

/*单条border样式*/
@mixin border-1px ($color, $direction) {
  position: relative;
  border: none;
  &::after{
    content: '';
    position: absolute;
    background: $color;
    @if $direction == left {
      left: 0;
      top: 0;
      height: 100%;
      width: 2px;
      transform: scaleX(0.5);
      transform-origin: left 0;
    }
    @if $direction == right {
      right: 0;
      top: 0;
      height: 100%;
      width: 2px;
      transform: scaleX(0.5);
      transform-origin: right 0;
    }
    @if $direction == bottom {
      bottom: 0;
      left: 0;
      width: 100%;
      height: 2px;
      transform: scaleY(0.5);
      transform-origin: 0 bottom;
    }
    @if $direction == top {
      top: 0;
      left: 0;
      width: 100%;
      height: 2px;
      transform: scaleY(0.5);
      transform-origin: 0 top;
    }
  }
}

/*四条border样式*/
@mixin all-border-1px ($color, $radius) {
  position: relative;
  border: none;
  &::after{
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    border: 2px solid $color;
    border-radius: $radius * 2;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    width: 200%;
    height: 200%;
    -webkit-transform: scale(0.5);
    transform: scale(0.5);
    -webkit-transform-origin: left top;
    transform-origin: left top;
  }

}
  • use
@import  " @assets/style/mixin.scss " ; // import

.box {
   @include  all-border-1px ( #eeeeee , 0 ); // use 
}

Configuration and use of vuex

  • Vuex
import { toRefs, reactive } from "vue";
import { useStore } from "vuex";
export default {

  setup() {
    const state = reactive({
      name: ''
    })  
    const store = useStore()

    state.name = store.state.Name

    return {
      ...toRefs(state)
    }
  }
};
  • Alternatives provide, inject

Declared once, globally accessible, and the data that needs to be shared is declared in advance through provide in the root node of Vue App.vue. First create a store

// src/store/store.ts
const planList = Symbol()
export default {
  planList,
}

Inject into outer components, such as provide in App.vue

// src/App.vue
<script lang="ts">
import Store from "./store/store"

import { defineComponent, provide, ref } from "@vue/composition-api"
export default defineComponent({
  setup() {
    provide(Store.planList, ref([]))
  }
})
</script>

Inject acceptance within the required components

// src/views/Plan.vue
<script lang="ts">
import Store from "./store/store"

import { defineComponent, provide, ref } from "@vue/composition-api"
export default defineComponent({
  setup() {
    const planList = inject(Store.planList)
    return {
      planList
    }
  }
})
</script>

Vue routing configuration and caching

  • Keep-alive change
 <router-view v-slot="{ Component }">
  <keep-alive>
    <component :is="Component" />
  </keep-alive>
</router-view>

tsconfig configuration

Set compileOnSave and sourceMap to false, if true, js and map files will be automatically generated when saving ts files

{
  "compileOnSave": false,
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": false,
    "baseUrl": ".",
    "types": [
      "webpack-env"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

Syntax detection automatic format code

  • eslintrc.js
module.exports = {
  root: true,
  env: {
    node: true
  },
  extends: [
    "plugin:vue/vue3-essential",
    "eslint:recommended",
    "@vue/typescript/recommended",
    "@vue/prettier",
    "@vue/prettier/@typescript-eslint"
  ],
  parserOptions: {
    ecmaVersion: 2020
  },
  rules: {
    "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
    "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off"
  }
};
  • vscode settings.json
{
     //  By default, vscode enables the option to automatically set tabsize according to file type 
    " editor.detectIndentation " : false ,
     //  Reset tabsize 
    " editor.tabSize " : 2 ,
     //  #Automatically format every time you save  
    " editor .formatOnSave " : true ,
     //  #Fix the code in eslint format every time you save it 
    " eslint.autoFixOnSave " : true ,
     //  Add  vue  support 
    " eslint.validate " : [
       " javascript ",
      "javascriptreact",
      {
        "language": "vue",
        "autoFix": true
      }
    ],
    //  #Let prettier use eslint's code format for verification  
    " prettier.eslintIntegration " : true ,
     //  #Remove the semicolon at the end of the code  
    " prettier.semi " : false ,
     //  #Use quotes instead of double quotes  
    " prettier. singleQuote " : true ,
     //  #Add a space between the function (name) and the following parenthesis 
    " javascript.format.insertSpaceBeforeFunctionParenthesis " : true ,
     //  #This is selected according to the user's own habits  
    " vetur.format.defaultFormatter.html " : " js-beautify-html ",
     //  ## Let the js in vue format according to the ts format that comes with the editor  
    " vetur.format.defaultFormatter.js " : " vscode-typescript " ,
     " vetur.format.defaultFormatterOptions " : {
       " js-beautify- html " : {
         " wrap_line_length " : 120 ,
         " wrap_attributes " : " auto "
         //  #vue component html code formatting style
      }
    },
    //  format the stylus,  to be installed's Manta  Stylus  Supremacy plug 
    " stylusSupremacy.insertColons " : false , //  whether to insert a colon 
    " stylusSupremacy.insertSemicolons " : false , //  whether to insert a semicolon 
    " stylusSupremacy.insertBraces " : false , //  whether Insert braces 
    " stylusSupremacy.insertNewLineAroundImports " : false , //  Whether to wrap after import 
    " stylusSupremacy.insertNewLineAroundBlocks " : false,
     " explorer.confirmDelete " : false  //  Whether to wrap in the two selectors 
  }

Download Details:

Author: weizhanzhan

Demo: https://vue3-ts-template-h5.vercel.app/home

Source Code: https://github.com/weizhanzhan/vue3-ts-template-h5

#vue #vuejs #javascript

Vue3.0 + Typescript first experience, create h5, webapp mobile
15.75 GEEK