如何用vue-cli3脚手架搭建一个基于ts的基础脚手架

目录

  • 准备工作
  • 搭建项目
  • vue 中 ts 语法
  • 项目代理及 webpack 性能优化
  • 其他

忙里偷闲,整理了一下关于如何借助 vue-cli3 搭建 ts + 装饰器 的脚手架,并如何自定义 webpack 配置,优化。

准备工作

  • @vue/[email protected]
  • vue 2.6
  • node v12.13.0

安装 node

  • 安装 node
  • 全局安装 nrm,npm 的镜像源管理工具。
npm i nrm -g // 安装
nrm ls // 查看可用源,及当前源,带*的是当前使用的源
nrm use taobao // 切换源,使用源
nrm add <registry> <url> // 其中reigstry为源名,url为源的路径
nrm del <registry> // 删除对应的源
nrm test npm // 测试源的响应速度

安装 vue-cli3

npm i @vue/cli -g // 全局安装

vue --version // 检查是否安装

补充

npm list -g --depth 0 // 查看全局安装的包
npm outdated -g --depth=0 // 查看需要更新的全局包
npm update 包名 -g // 更新全局安装的包

搭建项目

可参考:使用Vue-cli 3.0搭建Vue项目

新建一个基于 ts 的 vue 项目

vue create vue-cli3-ts

备注:如果是 window 系统,用 git bash 交互提示符(切换)不会工作,用以下命令,即可解决:

winpty vue.cmd create vue-cli3-ts
  • 自定义选项 - Manually select features
  • 添加 ts 支持 - TypeScript
  • 基于类的组件 - y
  • tslint
  • 根据需要添加 router、vuex、css(less 或 scss) 预处理器、单元测试(jest)

交互说明:

  • 上下箭头键切换
  • 空格键选中
  • 回车确定

在已存在的项目中添加 ts

vue add @vue/typescript

会把所有 .js 更改为 .ts

script 命令

// - 启动服务
npm run serve
// - 打包编译
npm run build
// - 执行lint
npm run lint
// - 执行单元测试
npm run test:unit

npm run serve 启动服务:http://localhost:8080/#/

vue 中 ts 语法

demo: src/components/HelloWorld.vue

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

@Component
export default class HelloWorld extends Vue {
  @Prop() private msg!: string;
}
</script>

和普通的 vue 项目不一样的就是.vue 文件中 script 的 写法。

主要用到的一个库:vue-property-decorator

用法可参考:

1. 类型注解,类型推论

  • 变量后面通过 冒号+类型 来做类型注解。
  • 编译时类型检查,写代码时代码提醒。
  • 类型推论,根据赋值类型推论出被赋值变量的类型,进行类型限制。
let title: string; // 类型注解
title = 'ts'; // 正确
title = 4; // 错误

let text = 'txt'; // 类型推论
text = 2; // 错误

错误时,vscode 编辑器会有红色波浪号提示。

数组

let names: string[]; // Array<string>
names = ['Tom'];

任意类型,没有类型限制

let foo: any;
foo = 'foo';
foo = 3;

let list: any[];
list = [1, true, 'free'];
list[1] = 100;

函数中使用类型

function greeting (person: string): string {
    return 'Hello, ' + person;
}

// void 类型,常用于没有返回值的函数
function warnUser (): void {
    alert('This is msg');
}

案例:vue demo

<template>
  <div class="hello">
    <input type="text" placeholder="请输入新特性" @keyup.enter="addFeature" />
    <ul>
      <li v-for="feature in features" :key="feature">{{feature}}</li>
    </ul>
  </div>
</template>

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

@Component
export default class Demo extends Vue {

  // 相当于 data 中的数据项
  features: string[];
  constructor () {
    super();
    this.features = ['类型注解', '类型推论', '编译型语言'];
  }

  // 相当于 methods 中的方法
  addFeature (event: any) {
    console.log(event);
    this.features.push(event.target.value);
    event.target.value = '';
  }
}
</script>

2.类

ts 中的类和 es6 中的大体相同,关注特性 访问修饰符

  • private 私有属性,不能在类的外部访问
  • protected 保护属性,可以在类的内部和派生类的内部访问,不能在类的外部访问
  • public 公有属性,可以在任意地方访问,默认值
  • readonly 只读属性,必须在声明时或构造函数里初始化,不可改变值

构造函数:初始化成员变量,参数加上修饰符,能够定义并初始化一个属性

constructor (private name = 'Tom') {
    super();
}

等同于

name: string;
constructor () {
    super();
    this.name = 'Tom';
}

存取器,暴露存取数据时可添加额外逻辑;在 vue 中可用作计算属性

get fullName () { return this.name; }
set fullName (val) { this.name = val; }

案例:vue demo

<template>
    <p>特性数量:{{count}}</p>
</template>
<script lang="ts">
    export default class Demo extends Vue {
        // 定义 getter 作为计算属性
        get count () {
            return this.features.length;
        }
    }
</script>

接口

接口仅约束结构,不要求实现

interface Person {
    firstName: string;
    lastName: string;
}
function greeting (person: Person) {
    return `Hello, ${person.firstName} ${person.lastName}`;
}
const user = {firstName: 'Jane', lastName: 'user'};
console.log(greeting(user));

案例:vue demo,声明接口类型约束数据结构

<template>
    <li v-for="feature in features" :key="feature.id">{{feature.name}}</li>
</template>
<script lang="ts">
    // 定义一个接口约束feature的数据结构
    interface Feature {
        id: number;
        name: string;
    }

    export default class Demo extends Vue {
        private features: Feature[];

        constructor () {
            super();

            this.features = [
                {id: 1, name: '类型注解'},
                {id: 2, name: '类型推论'},
                {id: 3, name: '编译型语言'}
            ]
        }
    }
</script>

泛型

泛型 是指在定义函数、接口或类的时候,不预先指定具体的类,而是在使用时才指定类型的一种特性。

interface Result<T> {
    data: T;
}

// 不使用泛型
interface Result {
    data: Feature[];
}

案例:使用泛型约束接口返回类型

function getData<T>(): Result<T> {
  const data: any = [
      {id: 1, name: '类型注解'},
      {id: 2, name: '类型推论'},
      {id: 3, name: '编译型语言'}
  ];
  return {data};
}

// 调用
this.features = getData<Feature[]>().data;

案例:使用泛型约束接口返回类型 Promise

function getData<T>(): Promise<Result<T>> {
  const data: any = [
      {id: 1, name: '类型注解'},
      {id: 2, name: '类型推论'},
      {id: 3, name: '编译型语言'}
  ];
  return Promise.resolve<Result<T>>({data});
}

// 调用 async 方式
async mounted () {
    this.features = (await getData<Feature[]>()).data;
}

// 调用 then 方式
mouted () {
    getData<Feature[]>().then((res: Result<Feature[]>) => {
      this.features = res.data;
    })
}

装饰器

装饰器用于扩展类或者它的属性和方法。

属性声明:@Prop

除了在 @Component 中声明,还可以采用@Prop的方式声明组件属性

export default class Demo extends Vue {
    // Props() 参数是为 vue 提供属性选项
    // !称为明确赋值断言,它是提供给ts的
    @Prop({type: String, require: true})
    private msg!: string;
}

事件处理:@Emit

// 通知父类新增事件,若未指定事件名则函数名作为事件名(驼峰变中划线分隔)
@Emit()
private addFeature(event: any) {//     若没有返回值形参将作为事件参数
    const feature = { name: event.target.value, id: this.features.length + 1 };
    this.features.push(feature);
    event.target.value = "";
    return feature;// 若有返回值则返回值作为事件参数
}

template 模板组件上正常写,@add-feature

变更监测:@Watch

@Watch('msg')
onRouteChange(val:string, oldVal:any){
    console.log(val, oldVal);
}

装饰器原理

装饰器本质是工厂函数,修改传入的类、方法、属性等

类装饰器

// 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
function log(target: Function) {
    // target是构造函数
    console.log(target === Foo); // true
    target.prototype.log = function() {
    console.log(this.bar);
}
// 如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明。
}
@log
class Foo {
    bar = 'bar'
}
const foo = new Foo();
// @ts-ignore
foo.log();

实战一下 Component,新建 Decor.vue

<template>
    <div>{{msg}}</div>
</template>
<script lang='ts'>
    import { Vue } from "vue-property-decorator";
    function Component(options: any) {
        return function(target: any) {
            return Vue.extend(options);
        };
    }

    @Component({
        props: {
            msg: {
                type: String,
                default: ""
            }
        }
    })
    export default class Decor extends Vue {}
</script>

源码简单了解

类装饰器主要依赖库:vue-class-component,深入源码,了解其背后究竟做了什么。

vue-property-decorator.js

import Vue from 'vue';
import Component, { createDecorator, mixins } from 'vue-class-component';
export { Component, Vue, mixins as Mixins };

createDecorator、applyMetadata 是核心,后续实现都依赖它,比如 Prop、Watch、Ref。

Prop 源码实现:

export function Prop(options) {
    if (options === void 0) { options = {}; }
    return function (target, key) {
        applyMetadata(options, target, key);
        createDecorator(function (componentOptions, k) {
            ;
            (componentOptions.props || (componentOptions.props = {}))[k] = options;
        })(target, key);
    };
}

applyMetadata,见名知义,就是将装饰器中的信息拿出来放到 options.type 中。

/** @see {@link https://github.com/vuejs/vue-class-component/blob/master/src/reflect.ts} */
var reflectMetadataIsSupported = typeof Reflect !== 'undefined' && typeof Reflect.getMetadata !== 'undefined';
function applyMetadata(options, target, key) {
    if (reflectMetadataIsSupported) {
        if (!Array.isArray(options) &&
            typeof options !== 'function' &&
            typeof options.type === 'undefined') {
            options.type = Reflect.getMetadata('design:type', target, key);
        }
    }
}

Reflect.getMetadata 获取设置在类装饰器上的元数据。可参考文章理解:

createDecorator,见名知义,就是创建装饰器。本质是在类上定义一个私有属性

export function createDecorator(factory) {
    return function (target, key, index) {
        var Ctor = typeof target === 'function'
            ? target
            : target.constructor;
        if (!Ctor.__decorators__) {
            Ctor.__decorators__ = [];
        }
        if (typeof index !== 'number') {
            index = undefined;
        }
        Ctor.__decorators__.push(function (options) { return factory(options, key, index); });
    };
}

项目代理及 webpack 性能优化

在项目根目录下新建 vue.config.js

本地开发 api 代理

module.exports = {
    devServer: {
        proxy: {
          '/api': {
            target: '<url>',
            changeOrigin: true,
            pathRewrite: {
              '^/api': ''
            }
          }
        }
    }
}

本地开发 api 模拟

devServer: {
    before (app) {
        before (app) {
            app.get('/api/getList', (req, res) => {
                res.json({data: [{id: 1, name: 'vue'}]})
            })
        }
    }
}

性能优化

查看打包依赖

在 package.json 文件 script 中加入命令:

"build:report": "vue-cli-service build --report"

会在 dist 目录下生成 report.html,可直接打开,查看打包依赖,进行分析,进行打包优化

打包优化 - cdn 引入公共库

在 vue.config.js 中加入配置:

configureWebpack: {
    externals: { // cdn 外链,避免包太大,首屏优化
      'vue': 'Vue',
      'vue-router': 'VueRouter',
      'vuex': 'Vuex'
    }
}

在 public/index.html 中加入 cdn 库地址

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.1.3/vue-router.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.2/vuex.min.js"></script>

<!-- built files will be auto injected -->

再次优化,html head 信息中加,dns 域名预解析,js 库 reload 预加载。

<link rel="dns-prefetch" href="cdnjs.cloudflare.com" >
<link href="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js" rel="preload" as="script">
<link href="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.1.3/vue-router.min.js" rel="preload" as="script">
<link href="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.2/vuex.min.js" rel="preload" as="script">

其他

修改本地开发端口号,在 vue.config.js 中加入配置:

devServer: {
    port: 8888
}

体验优化-打包完成提示:

const WebpackBuildNotifierPlugin = require('webpack-build-notifier');
const path = require('path');

module.exports = {
    // 链式操作
    chainWebpack: config => {
        // 移除 prefetch 插件,移动端对带宽敏感
        // 路由懒加载,只对用户频繁操作的路由,通过 注释 提前获取
        // component: () => import(/* webpackChunkName: "about" */ /* webpackPrefetch: true */'../views/About.vue')
        config.plugins.delete('prefetch');

        // 生产打包才提示,开发不提示
        if (process.env.NODE_ENV === 'production') {
          config.plugin('build-notify').use(WebpackBuildNotifierPlugin, [{
            title: "My Project Webpack Build",
            logo: path.resolve("./img/favicon.png"),
            suppressSuccess: true
          }])
        }
    }
}

原文地址:https://www.cnblogs.com/EnSnail/p/12024975.html

时间: 2024-10-27 18:48:17

如何用vue-cli3脚手架搭建一个基于ts的基础脚手架的相关文章

如何搭建一个基于 Egg + Webpack + Vue 的服务端渲染工程项目呢?

如何搭建一个基于 Egg + Webpack + Vue 的服务端渲染工程项目呢? 项目你可以通过 easywebpack-cli 直接初始化即可完成或者clone egg-vue-webpack-boilerplate.下面说明一下从零如何搭建一个Egg + Webpack + Vue 的服务端渲染工程项目. 通过 egg-init 初始化 egg 项目 egg-init egg-vue-ssr // choose Simple egg app 安装 easywebpack-vue 和 egg

如何用 Parse 和 Swift 搭建一个像 Instagram 那样的应用?(3)

[编者按]本篇文章作者是 Reinder de Vries,既是一名企业家,也是优秀的程序员,发表多篇应用程序的博客.本篇文章中,作者主要介绍了如何基于 Parse 特点,打造一款类似 Instagram 的应用,完整而清晰的步骤,为开发者提供一次绝佳的学习体验.本文系 OneAPM 工程师编译整理,这是本系列的第 3 篇文章. 如何用 Parse 和 Swift 搭建一个像 Instagram 那样的应用?(1) 如何用 Parse 和 Swift 搭建一个像 Instagram 那样的应用?

如何用 Parse 和 Swift 搭建一个像 Instagram 那样的应用?

[编者按]本篇文章作者是Reinder de Vries,既是一名企业家,也是优秀的程序员,发表多篇应用程序的博客.本篇文章中,作者主要介绍了如何基于Parse特点,打造一款类似Instagram的应用,完整而清晰的步骤,为开发者提供一次绝佳的学习体验.本文系 OneAPM 工程师编译整理. Parse 是一个移动应用开发平台,旗下有个很有意思的产品:Parse Core.它的特色之一是允许应用开发者直接将数据存储在云端,而无需担心设置服务器或重新设计一个 REST API.Parse Core

如何用 Parse 和 Swift 搭建一个像 Instagram 那样的应用?(2)

[编者按]本篇文章作者是 Reinder de Vries,既是一名企业家,也是优秀的程序员,发表多篇应用程序的博客.本篇文章中,作者主要介绍了如何基于 Parse 特点,打造一款类似 Instagram 的应用,完整而清晰的步骤,为开发者提供一次绝佳的学习体验.本文系 OneAPM 工程师编译整理,这是本系列的第 2 篇文章. 如何用 Parse 和 Swift 搭建一个像 Instagram 那样的应用?(1) 将数据展现在屏幕上 让我们看看下面这个类的最终方法.它会把数据放在表视图中: o

如何用最小的成本搭建一个优质网站

提起网站相信很多人对此并不陌生,网站是很多企业线上宣传产品的重要平台,通过网站可以展示企业产品.树立企业文化.维护企业形象的重要平台.在进行网站架设是需要注册域名.设计网站.进行服务器租用或托管等等,很多用户在进行网站建设时考虑最多的要数网站建设的费用问题,用最少的费用搭建优质的网站是很多用户的目标,那么,如何用最小的成本搭建一个优质网站? 一.注册合适域名 所谓域名就是说我们在网络上的地址,客户可以直接通过在浏览器上输入这个域名进入我们的网站,域名有免费的也有付费的,免费的域名一般为二级域名或

用vue-cli脚手架搭建一个仿网易云音乐的全家桶vue项目

一,vue-cli环境搭建 1,全局安装webpack npm install webpack -g 2,安装vue脚手架 npm install vue-cli -g 3,新建一个新的project,终端定位到该项目下 4,初始化项目 vue init webpack-simple 会让你进行该项目的一些基本设置,如下图 5,安装项目依赖 npm install 6,启动项目 npm run dev 可以看到终端提示成功 在浏览器上打开localhost:8080/#/可以看到如下图所示,说明

如何使用脚手架搭建一个react项目

1.准备工作: 首先要知道,搭建一个react项目总是需要安装node.js和npm的,未安装的请移步node.js官网:https://nodejs.org/en/ 检测是否安装成功:npm -v 和node -v 2.npm安装脚手架 执行命令npm install create-react-app 3.使用npx和脚手架创建项目my_app,这个过程可能需要几分钟,请耐心等待 npx create-react-app my_app 这里用到了npx,就是可以让你不用全局安装create-r

【TGUI】从零开始搭建一个基于Unity的UI库 01

作为一个初学Unity3D一个来月的小菜鸟,要想自己搭建一个UI库也是挺疯狂的,但是老大不给用NGUI,然后那时候Unity4.6正式版还没发布(或者说我还不知道Unity4.6有自带UI),然后就无所畏惧的写下来了,目前虽然功能很简陋,实现的控件也非常少,但是作为练手的目的已经达成了,所以放上来给大家分享. -------------------------------------------------- 分割线 ---------------------------------------

如何利用Facebook的create-react-app脚手架创建一个基于ant design mobile的项目

引言: create-react-app是Facebook发布的一款全局的命令行工具用来创建一个新的项目. 通常我们开始做一个react web或者 app 项目的时候,都会自己利用 npm 或者 yarn 安装项目所需要的一些依赖,再写 webpack.config.js ,一系列复杂的配置,搭建好开发环境后写src源代码. 现在,如果你想要搭建一个完整的 react 项目环境,已经不需要自己动手布置许许多多的东西,利用 create-react-app 工具,就能轻松帮你配置好一个 reac