preface

This article shows you how to build and develop projects using TypeScript in Vue projects, and the pitfalls you’ll tread in the process.

With a type system and a superset of JavaScript, TypeScript is gaining momentum in 2018.

Vue3.0 will be rewritten using TS. The rewritten Vue3.0 will better support TS. As TypeScript becomes more common in 2019, being familiar with TS and having worked on projects with IT will become even more of an advantage for front-end developers.

So the author of course also want to learn this essential skill, to learn while practicing the way, do a blog project to play.

This project is based on the Vue family bucket + TypeScript + element-UI stack, and is open source, github address blog-vue-typescript.

Because I wrote an article about the construction of pure Vue project based on the description of the mobile-H5 project of Vue + Mint-UI before, many people added me to wechat and asked for source code to learn, but this is our company’s project, so we cannot provide the original code.

So do a project that is not our company’s and is related to VUE to practice and open source.

Effect of 1.

Effect:

  • PC

  • The mobile terminal

For the full effect, see biaochenxuying.cn

Function of 2.

Completed function

Square root login

Tick the registered

√ List of articles

√ Archiving

Square root tag

Square root of

√ Likes and comments

Tick the message

Square root course

√ Article details (support code syntax highlighting)

√ Contents of articles

√ Mobile adaptation

√ Github authorized login

To be optimized or implemented

X use vuex – class

X More TypeScript optimization tips

X server rendering SSR

3. Main front-end technologies

All technologies are current.

  • Vue: ^ 2.6.6
  • The typescript: ^ 3.2.1
  • Element – the UI: 2.6.3
  • Vue – the router: ^ 3.0.1
  • Webpack: 4.28.4
  • Vuex: ^ 3.0.1
  • Axios: 0.18.0
  • Highlight. Js: 9.15.6
  • Marked: 0.6.1

Get started with TypeScript in 5 minutes

Non-typescript readers won’t be able to follow the rest of the TypeScript class without some basic knowledge, so learn the basics first.

TypeScript’s static type checking is a good thing because it avoids many unnecessary errors that are discovered during debugging or when a project goes live.

  • Type annotations

Type annotations in TypeScript are a lightweight way to add constraints to functions or variables. When a variable is defined, its type should also be defined, such as the common:

// let isDone: Boolean = false; // let isDone = false; Let decLiteral: number = 6; let decLiteral: number = 6; // let decLiteral = 6; // string let name: string = "Bob "; // let name = "Bob "; Let list: number[] = [1, 2, 3]; let list: number[] = [1, 2, 3] // let list = [1, 2, 3]; // The second way is to use Array generics, Array< element type > : let list: Array<number> = [1, 2, 3]; // let list = [1, 2, 3]; // In TypeScript, we use Interfaces to define the types of objects. interface Person { name: string; age: number; } let tom: Person = { name: 'Tom', age: 25 }; // Let Tom = {name: 'Tom', age: 25}; // The type of Any can be changed arbitrarily (when the value might come from dynamic content, such as user input or third-party code libraries) let notSure: Any = 4; NotSure = false; Void function warnUser(): Void {console.log("This is my warning message"); } // any function fetch(url: string, id: number, params: any): void {console.log("fetch"); }Copy the code

These are some of the easiest things to learn. See TypeScript’s Chinese website for more information

Get started with Vue +TypeScript in 5 minutes

  • vue-class-component

    Vue – class – component ofVueThe component has a layer of encapsulation that letsVueComponent syntax is coming togetherTypeScriptGrammar is even flatter later:
<template> <div> <input v-model="msg"> <p>prop: {{propMessage}}</p> <p>msg: {{msg}}</p> <p>helloMsg: {{helloMsg}}</p> <p>computed msg: {{computedMsg}}</p> <button @click="greet">Greet</button> </div> </template> <script> import Vue from 'vue' import Component from 'vue-class-component' @Component({ props: { propMessage: String } }) export default class App extends Vue { // initial data msg = 123 // use prop values for initial data helloMsg = 'Hello, ' + this.propMessage // lifecycle hook mounted () { this.greet() } // computed get computedMsg () { return 'computed ' +  this.msg } // method greet () { alert('greeting: ' + this.msg) } } </script>Copy the code

The above code does the same as the following:

<template> <div> <input v-model="msg"> <p>prop: {{propMessage}}</p> <p>msg: {{msg}}</p> <p>helloMsg: {{helloMsg}}</p> <p>computed msg: {{computedMsg}}</p> < button@click ="greet"> </button> </div> </template> <script> export default {// attribute props: { propMessage: { type: String } }, data () { return { msg: 123, helloMsg: 'Hello, '+ this.propMessage}}, // declare cycle hook mounted () {this.greet()}, // Calculate attributes computed: {computedMsg () {return 'computed '+ this.msg}}, // methods: {greet () {alert('greeting: ' + this.msg) } }, } </script>Copy the code
  • vue-property-decorator

Vue-property-decorator is an enhanced vUE decorator on vue-class-Component. These 7 decorators are added:

  • @Emit
  • @Inject
  • @Model
  • @Prop
  • @Provide
  • @Watch
  • @Component(fromvue-class-componentInheritance)

Here are some commonly used @prop / @watch /@Component. For more information, see the official documentation

import { Component, Emit, Inject, Model, Prop, Provide, Vue, Watch } from 'vue-property-decorator'

@Component
export class MyComponent extends Vue {

  @Prop()
  propA: number = 1

  @Prop({ default: 'default value' })
  propB: string

  @Prop([String, Boolean])
  propC: string | boolean

  @Prop({ type: null })
  propD: any

  @Watch('child')
  onChildChanged(val: string, oldVal: string) { }
}
Copy the code

The code above is equivalent to:

export default {
  props: {
    checked: Boolean,
    propA: Number,
    propB: {
      type: String,
      default: 'default value'
    },
    propC: [String, Boolean],
    propD: { type: null }
  }
  methods: {
    onChildChanged(val, oldVal) { }
  },
  watch: {
    'child': {
      handler: 'onChildChanged',
      immediate: false,
      deep: false
    }
  }
}
Copy the code
  • vuex-class

    vuex-classIn:vue-class-componentWritten bindingvuex
import Vue from 'vue'
import Component from 'vue-class-component'
import {
  State,
  Getter,
  Action,
  Mutation,
  namespace
} from 'vuex-class'

const someModule = namespace('path/to/module')

@Component
export class MyComp extends Vue {
  @State('foo') stateFoo
  @State(state => state.bar) stateBar
  @Getter('foo') getterFoo
  @Action('foo') actionFoo
  @Mutation('foo') mutationFoo
  @someModule.Getter('foo') moduleGetterFoo

  // If the argument is omitted, use the property name
  // for each state/getter/action/mutation type
  @State foo
  @Getter bar
  @Action baz
  @Mutation qux

  created () {
    this.stateFoo // -> store.state.foo
    this.stateBar // -> store.state.bar
    this.getterFoo // -> store.getters.foo
    this.actionFoo({ value: true }) // -> store.dispatch('foo', { value: true })
    this.mutationFoo({ value: true }) // -> store.commit('foo', { value: true })
    this.moduleGetterFoo // -> store.getters['path/to/module/foo']
  }
}
Copy the code

6. Set up the project with VUe-CLI

The author uses the latest vuE-CLI 3 construction project. For a detailed tutorial, please refer to my previous article about vue-CLI3. x new features and trampling, which has been explained in detail. However, the configuration in this article is different from this project, because I added TypeScript. The rest of the configuration is vue-CLI originally prepared. For details, see vue-CLI official website.

6.1 Installing and Building the Project Directory

Install dependencies:


Some configurations selected during the installation process:


Once set up, the initial project structure looks like this:

├─ public // Static page ├─ SRC // Home Directory ├─ Assets // Static Resources ├─ Components // Views // Page ├─ app.vue // page Main import ├─ Main. Ts / / foot benzhu entrance ├ ─ ─ the router. The ts / / routing ├ ─ ─ shims - TSX. Which s / / TSX composite module into ├ ─ ─ shims - vue. Which s / / vue modules into └ ─ ─ store. The ts / / Vuex Configure ├─ tests // Test Cases ├─.eslintrc.js // esLint related Configure ├─.gitignore // gitignore file Configure ├─ babel.config.js // Babel configure ├ ─ ─ postcss. Config. Js/configuration/postcss ├ ─ ─ package. The json / / dependence └ ─ ─ tsconfig. Json / / ts configurationCopy the code

Aiming at the structure of large projects to transform the project structure, after transformation:

├─ public // Static Page ├─ SRC // Home Directory ├─ Assets // Static Resources ├── filters // Filter ├── store // Vuex Config ├─ less // Style ├─ utils // Tool approach (AXIos encapsulation, ├─ views // Page ├─ app.vue // page Main import ├─ main.ts // Main import ├─ Router.ts // Route ├─ local.d.ts // Related Global or plugin ├─ Shims-Vue.d.ts // Some TSX modules have been added to the system. Make TypeScript support *.vue files ├─ tests // Test Cases ├─.eslintrc.js // ESLint related Configuration ├─ postcss.config.js // PostCSS Configuration ├─ .gitignore // gitignore File Setup ├── Bass.config. js // Preset Record ├─ package.json // Rely On ├─ readme.md // Project README ├─ Json // tsconfig ├ ─ ├.config.js // webpackCopy the code

The tsconfig.json file specifies the root file and compilation options to compile this project. The tsconfig.json configuration of this project is as follows:

{// Compile options "compilerOptions": {// Compile output target ES version "target": "esnext", // module system "module": "esnext", // parse "strict": True, "JSX ": "preserve", // Import external help libraries from tSLIb such as __extends, __rest "importHelpers": true, // How to handle the module "moduleResolution": "Node ", // enable the decorator "experimentalDecorators": true, "esModuleInterop": True, / / allow never set default default import export modules "allowSyntheticDefaultImports" : true, / / define a variable, you must give it an initial value "strictPropertyInitialization" : False, // Allow compiling javascript file "allowJs": true, // Whether to include sourceMap that can be used for debugging "sourceMap": Raise error on this expressions with an implied any type. "noImplicitThis": False, // Parse the base directory for non-relative module names "baseUrl": ".", // style errors and messages, using color and context. "Pretty" : true, / / set the introduction of the definition file "types" : [" webpack - env ", "mocha", "chai"], / / the path of the designated special module "paths" : {" @ / * ": Lib: ["esnext", "dom", "dom. Iterable ", "scripthost"]}, // ts manage file "include": ["/SRC / * * * ts "and"/SRC / * * *. The TSX ", "/ SRC / * * *. Vue", "tests / / *. * * ts" and "tests / / * * *. The TSX"], / / ts to exclude files "exclude" : ["node_modules"] }Copy the code

See the tsconfig.json compilation options on the official website for more configuration

Vue.config. js of this project:

const path = require("path"); const sourceMap = process.env.NODE_ENV === "development"; Module.exports = {// base path publicPath: ". ", // outputDir: "dist", // eslint-loader checks lintOnSave when saving: False, / / / / webpack configuration see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md chainWebpack: () => {}, configureWebpack: config => {if (process.env.node_env === "production") {// Modify the configuration for production... config.mode = "production"; } else {// Modify the configuration for the development environment... config.mode = "development"; } Object. The assign (config, {/ / development and production of common configuration resolve: {extensions: [", "vue" js ", "json", "ts" and "benchmark"), alias: {vue $: "vue/dist/vue.js", "@": path.resolve(__dirname, "./src") } } }); }, // Whether the production environment generates the sourceMap file productionSourceMap: sourceMap, // CSS related configuration CSS: {// Whether to use CSS ExtractTextPlugin extract: True, // Enable CSS source maps? SourceMap: false, // CSS preset configuration item loaderOptions: {}, // enable CSS modules for all CSS/pre-processor files.modules: false }, // use thread-loader for babel & TS in production build // enabled by default if the machine has more than 1 cores parallel: require("os").cpus().length > 1, / / PWA plug-in configuration / / see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa PWA: DevServer: {open: process.platform === "Darwin ", host: "localhost", port: 3001, //8080, https: false, hotOnly: false, proxy: {// proxy all requests starting with/API to jsonplaceholder "/ API ": {// target: "https://emm.cmccbigdata.com:8443/", target: "http://localhost:3000/", // target: "http://47.106.136.114/", changeOrigin: true, ws: true, pathRewrite: {"^/ API ": "}}}, before: App => {}}, // pluginOptions: {//... }};Copy the code

6.2 installation element – the UI

I was going to use it with iView-UI, but LATER I wanted to make this project INTO SSR, Vue + typescript + iView + Nuxt.js server rendering is a bit of a mess, and Vue + typescript + Element + nuxt.js already supports SSR. So we chose Element-UI.

Installation:

npm i element-ui -S
Copy the code

Import on demand, with the babel-plugin-Component, we can import only the components needed to reduce the size of the project.

npm install babel-plugin-component -D
Copy the code

Then, change babel.config.js to:

module.exports = {
  presets: ["@vue/app"],
  plugins: [
    [
      "component",
      {
        libraryName: "element-ui",
        styleLibraryName: "theme-chalk"
      }
    ]
  ]
};
Copy the code

Next, if you want to introduce only a few components, such as Button and Select, you need to write the following in main.js:

import Vue from 'vue'; import { Button, Select } from 'element-ui'; import App from './App.vue'; Vue.component(Button.name, Button); Vue.component(Select.name, Select); Use (Button) * Vue. Use (Select) */ new Vue({el: '#app', render: h => h(app)});Copy the code

6.3 Improve project directories and files

route

Use the route lazy loading function.

export default new Router({
  mode: "history",
  routes: [
    {
      path: "/",
      name: "home",
      component: () => import(/* webpackChunkName: "home" */ "./views/home.vue")
    },
    {
      path: "/articles",
      name: "articles",
      // route level code-splitting
      // this generates a separate chunk (articles.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () =>
        import(/* webpackChunkName: "articles" */ "./views/articles.vue")
    },
  ]
});
Copy the code

utils

  • Utils /utils. Ts encapsulates common functions, such as event throttling and debounce methods:
// fn is the event callback we need to package, delay is the time threshold export function throttle(fn: function, delay: Let last = 0, timer: any = null; // return function() {// retain this context when called let context = this; // Retain the arguments passed in when calling let args = arguments; Let now = +new Date(); If (now-last < delay) {// If (now-last < delay) {// If (now-last < delay) {// If (now-last < delay) {// If (now-last < delay) {// If (now-last < delay) {// If (now-last < delay) {// If (now-last < delay);  timer = setTimeout(function() { last = now; fn.apply(context, args); }, delay); } else {// If the interval exceeds the interval threshold we set, it will not be equal. fn.apply(context, args); }}; }Copy the code
  • Utils /config.ts configuration files, such as github authorized login callback address, client_id, client_secret, etc.
const config = { 'oauth_uri': 'https://github.com/login/oauth/authorize', 'redirect_uri': 'https://biaochenxuying.cn/login', 'client_id': 'XXXXXXXXXX', 'client_secret': 'XXXXXXXXXX', }; / / if local development environment (process. The env. NODE_ENV = = = 'development') {config. Redirect_uri = "http://localhost:3001/login" config.client_id = "502176cec65773057a9e" config.client_secret = "65d444de381a026301a2c7cffb6952b9a86ac235" } export default config;Copy the code

If your production environment requires github login authorization, please apply for an Oauth App on Github and fill in the config file with your redirect_URI, client_ID, and client_secret information. How to design a user table for a third party authorized login

  • Utils /urls.ts requests interface addresses for unified management.
Export const urls: object = {login: "login", register: "register", getArticleList: "getArticleList",}; export default urls;Copy the code
  • Utils/HTTPS. Ts Encapsulates the axios request.
import axios from "axios"; // create an axios instance let service: any = {}; Service = axios.create({baseURL: "/ API ", // API base_URL timeout: 50000 // request timeout time}); / / request interceptor axios some of the configuration service. The interceptors. Request. Use ((config: any) = > {return config. }, (error: any) => { // Do something with request error console.error("error:", error); // for debug Promise.reject(error); }); / / respone interceptor axios some of the configuration service. The interceptors. Response. Use ((response: any) = > {return response; }, (error: any) => { console.error("error:" + error); // for debug return Promise.reject(error); }); export default service;Copy the code

Attach urls and HTTPS to Vue prototype in main.ts.

import service from "./utils/https"; import urls from "./utils/urls"; Vue.prototype.$https = service; Prototype.$urls = urls; // Other pages use this.$urls instead of thisCopy the code

Then you can unify the management interface and make it easy to call. Consider the request for the article list below.

async handleSearch() { this.isLoading = true; const res: any = await this.$https.get(this.$urls.getArticleList, { params: this.params }); this.isLoading = false; if (res.status === 200) { if (res.data.code === 0) { const data: any = res.data.data; this.articlesList = [...this.articlesList, ...data.list]; this.total = data.count; this.params.pageNum++; if (this.total === this.articlesList.length) { this.isLoadEnd = true; } } else { this.$message({ message: res.data.message, type: "error" }); }} else {this.$message({message: "network error!") , type: "error" }); }}Copy the code

store ( Vuex )

Generally large projects have many modules, such as this project has public information (such as token), user module, article module.

├── ├─ exercises, exercises, exercises, exercises, exercises, exercises, exercises, exercises, exercises, exercisesCopy the code
  • Store /index.ts stores public information and imports it into other modules
import Vue from "vue"; import Vuex from "vuex"; import * as types from "./types"; import user from "./modules/user"; import article from "./modules/article"; Vue.use(Vuex); const initPageState = () => { return { token: "" }; }; const store = new Vuex.Store({ strict: process.env.NODE_ENV ! Mutations: {[types.SAVE_TOKEN](mutations: {[types. any, pageState: any) { for (const prop in pageState) { state[prop] = pageState[prop]; } } }, actions: {} }); export default store;Copy the code
  • types.ts
// public token export const SAVE_TOKEN = "SAVE_TOKEN"; // user export const SAVE_USER = "SAVE_USER";Copy the code
  • user.ts
import * as types from ".. /types"; const initPageState = () => { return { userInfo: { _id: "", name: "", avator: "" } }; }; const user = { state: initPageState(), mutations: { [types.SAVE_USER](state: any, pageState: any) { for (const prop in pageState) { state[prop] = pageState[prop]; } } }, actions: {} }; export default user;Copy the code

7. Markdown rendering

Markdown rendering:


The markdown rendering is marked with open source, and the code is highlighted with highlight.js.

Usage:

NPM I marked highlight.js –save

npm i marked highlight.js --save
Copy the code

Step 2: Import and encapsulate as markdown.js, convert the article details from string to HTML, and extract the article contents.

Marked thanks to the old man.

const highlight = require("highlight.js"); const marked = require("marked"); const tocObj = { add: function(text, level) { var anchor = `#toc${level}${++this.index}`; this.toc.push({ anchor: anchor, level: level, text: text }); return anchor; }, // Use stack to handle ul,li, level 1 is the outermost layer / / < ul > / / < li > < / li > / / < ul > / / < li > < / li > / / < / ul > / / < li > < / li > / / < / ul > toHTML: function() { let levelStack = []; let result = ""; const addStartUL = () => { result += '<ul class="anchor-ul" id="anchor-fix">'; }; const addEndUL = () => { result += "</ul>\n"; }; const addLI = (anchor, text) => { result += '<li><a class="toc-link" href="#' + anchor + '">' + text + "<a></li>\n"; }; this.toc.forEach(function(item) { let levelIndex = levelStack.indexOf(item.level); If (levelIndex === -1) {levelStack.unshift(item.level); addStartUL(); addLI(item.anchor, item.text); Else if (levelIndex === 0) {addLI(item.anchor, item.text); } else {while (levelIndex--) {levelStack.shift();} else {levelIndex--) {levelStack.shift(); addEndUL(); } addLI(item.anchor, item.text); }}); While (levelStack.length) {levelStack.shift(); addEndUL(); } // Clean up the previous data for the next use of this.toc = []; this.index = 0; return result; }, toc: [], index: 0 }; class MarkUtils { constructor() { this.rendererMD = new marked.Renderer(); this.rendererMD.heading = function(text, level, raw) { var anchor = tocObj.add(text, level); return `<h${level} id=${anchor}>${text}</h${level}>\n`; }; highlight.configure({ useBR: true }); marked.setOptions({ renderer: this.rendererMD, headerIds: false, gfm: true, tables: true, breaks: false, pedantic: false, sanitize: false, smartLists: true, smartypants: false, highlight: function(code) { return highlight.highlightAuto(code).value; }}); } async marked(data) { if (data) { let content = await marked(data); // Let toc = tocobj.tohtml (); Return {content: content, toc: toc}; } else { return null; } } } const markdown = new MarkUtils(); export default markdown;Copy the code

Step 3: Use

import markdown from "@/utils/markdown"; Async handleSearch() {const res: any = await this.$HTTPS. Post (this.$urls.getarticledetail, this.params); if (res.status === 200) { if (res.data.code === 0) { this.articleDetail = res.data.data; Marked (res.data.data.content); // Use marked to convert const article = markdown.marked(res.data.data.content); article.then((response: any) => { this.articleDetail.content = response.content; this.articleDetail.toc = response.toc; }); } else { // ... } else { // ... } // render <div id="content" class="article detail" v-html=" articledetail. content">Copy the code

Step 4: Introduce the CSS style of Monokai_sublime

< link href = "http://cdn.bootcss.com/highlight.js/8.0/styles/monokai_sublime.min.css" rel = "stylesheet" >Copy the code

Step 5: Supplement the Markdown style

If you do not add styles, there will be no black background, the font size will be small, and the image will not be centered

/* Complement to markdown */ pre {display: block; padding: 10px; margin: 0 0 10px; font-size: 14px; The line - height: 1.42857143; color: #abb2bf; background: #282c34; word-break: break-all; word-wrap: break-word; overflow: auto; } h1,h2,h3,h4,h5,h6{ margin-top: 1em; /* margin-bottom: 1em; */ } strong { font-weight: bold; } p > code:not([class]) { padding: 2px 4px; font-size: 90%; color: #c7254e; background-color: #f9f2f4; border-radius: 4px; } p img{/* image center */ margin: 0; display: flex; } #content { font-family: "Microsoft YaHei", 'sans-serif'; font-size: 16px; line-height: 30px; } #content .desc ul,#content .desc ol { color: #333333; Margin: 1.5em 0 0 25px; } #content .desc h1, #content .desc h2 { border-bottom: 1px solid #eee; padding-bottom: 10px; } #content .desc a { color: #009a61; }Copy the code

8. Pay attention to the point

  • On the page

For the page about, in fact, is an article, according to the article type type to decide, the database of type 3 articles, there can only be one is the introduction of the blogger; You can change the content whenever you want.

So when the current route === ‘/about’ is the request type is introduced by the blogger.

Type: 3, // Article type: 1: normal article; 2. the resume of the blogger. 3. Profile of the blogger;Copy the code
  • Mobile adaptation The mobile uses REM unit adaptation.
// Screen fit (window.screen.width/mobile design width *100) is also (window.screen.width / 750 *100) -- *100 for easy calculation. Namely the font size value is mobile phone deviceWidth and design draft ratio of 100 times the document. The getElementsByTagName (' HTML ') [0]. Style. FontSize = window. The screen. The width / 7.5 + 'px;Copy the code

As shown above: Dynamically set the HTML font size by querying the screen width. Most mobile designs are set to be 750 px wide.

For example, a 150 × 250 box (px) on the drawing:

Originally written in CSS:

width: 150px;
heigth: 250px;
Copy the code

After the above conversion, the corresponding REM value in CSS only needs to be written:

Width: 1.5 rem; // 150/100 REM heigth: 2.5rem; // 250 / 100 remCopy the code

If your mobile design is 1080px wide, use window.screen.width / 10.8.

9. Hit the pit

  • 1. Make VUE recognize global methods/variables
  1. We often mount vue.prototype instances or contents in main.ts for easy use in components.
import service from "./utils/https"; import urls from "./utils/urls"; Vue.prototype.$https = service; Prototype.$urls = urls; // Other pages use this.$urls instead of thisCopy the code

However, you will get an error when you direct this. HTTP or this.urls in a component, because the HTTP and urls properties are not declared in the vue instance.

  1. Or meesage using Element-UI.
import { Message } from "element-ui";

Vue.prototype.$message = Message;
Copy the code

Previous usage:

This.$message({message: 'this is a successful message ', type: 'success'})Copy the code

However, errors can still be reported.

Another example is listening for route changes:

import { Vue, Watch } from "vue-property-decorator";
import Component from "vue-class-component";
import { Route } from "vue-router";

@Component
export default class App extends Vue {

  @Watch("$route")
  routeChange(val: Route, oldVal: Route) {
      //  do something
  }
}
Copy the code

Listening for $route will still fail if it is written this way.

If you want the above three methods to work properly, you need to add the following:

Add the contents to be mounted to shims-vue.d.ts under SRC. Vue has these things under this.

import VueRouter, { Route } from "vue-router"; declare module "vue/types/vue" { interface Vue { $router: VueRouter; $route: route; $https: any; $urls: any; $Message: any; }}Copy the code
  • 2. The imported modules must be declared

Document or Document. querySelector will return an error, and NPM run build will not pass.

Another example: components and animation components that reference Element on demand:

import { Button } from "element-ui";
import CollapseTransition from "element-ui/lib/transitions/collapse-transition";
Copy the code

NPM run serve can be executed, but in NPM run build, the error will be reported directly, because there is no declaration.

Do it right:

Create a new file shime-global.d.ts under SRC and add the following:

// Declare var window: window; // Declare var window: window; declare var document: Document; declare module "element-ui/lib/transitions/collapse-transition"; declare module "element-ui";Copy the code

Of course, you can add this file in other places, any other name is OK.

However, even after the above method is configured, there are some places that use document.XXX, such as document.title, NPM run build still cannot pass, so it is left:

<script lang="ts"> // Declare var document: any; </script>Copy the code
  • 3. Type check for this

For example, throttling and debounce methods for previous events:

Export function throttle(fn: function, delay: number) {return function() {// save this context let context = this; }Copy the code

This in function will get an error when NPM runs serve because Tyescript detects that it is not in a class.

Do it right:

Add “noImplicitThis”: false to the tsconfig.json file in the root directory, ignoring the type check for this.

// Raise error on this expressions with an implied any type. "noImplicitThis": false,Copy the code
  • 4. Import.vue files

When importing. Vue files, complete the. Vue suffix, otherwise NPM run build will report an error.

Such as:

import Nav from "@/components/nav"; // @ is an alias to /src
import Footer from "@/components/footer"; // @ is an alias to /src
Copy the code

To change to:

import Nav from "@/components/nav.vue"; // @ is an alias to /src
import Footer from "@/components/footer.vue"; // @ is an alias to /src
Copy the code
  • 5. Decorator @Component

An error.

<script lang="ts">
import { Vue, Component } from "vue-property-decorator";
export default class LoadingCustom extends Vue {}
</script>
Copy the code

The following is correct because the Vue here comes from the VUe-property-decorator import.

<script lang="ts">
import { Vue, Component } from "vue-property-decorator";

@Component
export default class LoadingCustom extends Vue {}
</script>
Copy the code
  • 6. The component navigation guard of the route fails

Vue-class-component uses Adding Custom Hooks that have no effect

The route navigation hook does not belong to the Vue itself, which will cause the class component to escape to the configuration object when the navigation hook is invalid. Therefore, if you want to use the navigation hook, you need to declare it in the router configuration.

  • 7. Tsconfig. Json strictPropertyInitialization set to false, you define a variable, you must give it an initial value.
  • position: sticky;

The directory of the article details in this project is sticky.

.anchor {
  position: sticky;
  top: 213px;
  margin-top: 213px;
}
Copy the code

Position :sticky is a new attribute for CSS positioning. It can be said that the combination of relative positioning and fixed positioning; It is mainly used to monitor scroll events; In simple terms, in the sliding process, when the distance between an element and its parent reaches the sticky sticky positioning requirement (for example, top: 100px); Position :sticky

The usage can be used as above, but there are conditions of use:

1. The parent element cannot have overflow:hidden or overflow: Auto. 3. The height of the parent element cannot be lower than that of the sticky element. 4

  • 8. Eslint reports files and decorators not found

App.vue is just a reference file, and webpack and tsconfig.josn already have aliases configured.

import Nav from "@/components/nav.vue"; // @ is an alias to /src
import Slider from "@/components/slider.vue"; // @ is an alias to /src
import Footer from "@/components/footer.vue"; // @ is an alias to /src
import ArrowUp from "@/components/arrowUp.vue"; // @ is an alias to /src
import { isMobileOrPc } from "@/utils/utils";
Copy the code

However, the following error is still reported:


However, the code does not affect the file packaging, and the local and production environment of the code is normal, no errors.

This ESLint check has not yet found the configuration to remove these errors.

  • 9. Change the routing mode to History

If the route mode is hash, the route to the article details page is #articleDetail. If the route mode is hash, the route to the article details page is #title2. #title2 will be added after #articleDetail, and the page will not be found when refreshed.

10. Build Setup

// git clone git clone https://github.com/biaochenxuying/blog-vue-typescript.git // cd cd blog-vue-typescript // install  dependencies npm install // Compiles and hot-reloads for development npm run serve // Compiles and minifies for production npm run buildCopy the code

If you want to see the complete effect of the background data, it is necessary to run with the background project blog- Node, otherwise the interface request will fail.

Although the mock has been introduced, I haven’t had time to do the simulation data. If you want to see the specific effect, please check biaochenxuying.cn on my website steadily

11. Project address and series of related articles

Based on Element Vue + TypeScript + blog – Vue – TypeScript front desk display: https://github.com/biaochenxuying/blog-vue-typescript

React + node + Express + ant + mongodb https://github.com/biaochenxuying/blog-react

Recommended reading:

Series of articles on this blog system:

  • React + Node + Express + Ant + mongodb
  • React + Ant Design + Support markdown blog-React project documentation
  • 3. Blog – Node project documentation based on Node + Express + mongodb
  • 4. As a small white server, how did I deploy node+mongodb project on the server and optimize its performance
  • 5. Github Authorized login tutorial and how to design a third party authorized login user table
  • 6 a website performance optimization road – the world martial arts, only fast not broken
  • 7. Vue + TypeScript + Element build a simple and stylish blog site and trite memory

12. The final

The author is also a beginner of TS, if the article has the wrong place, please point out, thank you.

I was also very resistant to building with Vue + TS at first, because there were a lot of holes and a lot of type checking was annoying. After the solution, understand the principle, is more with more cool, ha ha.


Weigh the

How to make better use of JS dynamic and TS static characteristics, we need to combine the actual situation of the project to make a comprehensive judgment. Some suggestions:

  • If it’s a small to medium size project with a short life cycle, use JS instead of getting stuck with TS.
  • For large applications with a long life cycle, you are advised to try TS.
  • If it is a public module such as a framework or library, then TS is recommended.

Whether to use TS depends on the actual project size, project life cycle, team size, team members and other actual conditions.

This is a small project that’s not really a good fit for joining TypeScript, but it’s a personal project and a hands-on one, so it doesn’t hurt.

Class-compoent will become mainstream in the future, making it easier to write TypeScript now and migrate to 3.0 later.

After work every day, I will spend several nights to write this article. It is not easy to code and write. If you think this article is good or helpful to you, please give me a thumbs up or a star.

Reference article:

  1. Vue + typescript projects start manually
  2. TypeScript + Working on large projects

Welcome to pay attention to the following public number, learn different martial arts secrets!

Python, Java, Linux, Go, Node, Vue, React, javaScript, etc