ome/img/nav3_on.png">
APP
系统平台
  • 建站知识
  • 联系我们
  • 咨询热线 :
    028-86922220

    疆括仕网站建设,新征程启航

    为企业提供网站建设、域名注册、服务器等服务

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

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

    创新互联-专业网站定制、快速模板网站建设、高性价比梅河口网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式梅河口网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖梅河口地区。费用合理售后完善,10年实体公司更值得信赖。

    准备工作

    • @vue/cli@4.1.1
    • vue 2.6
    • node v12.13.0

    安装 node

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

    安装 vue-cli3

    参考官方文档:https://cli.vuejs.org/zh/guide/

    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

    
    
    

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

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

    用法可参考:

    • npm
    • vue-property-decorator用法
    • ts 官方文档

    1. 类型注解,类型推论

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

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

    数组

    let names: string[]; // Array
    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

    
    
    
    
    

    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

    
    
    

    接口

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

    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,声明接口类型约束数据结构

    
    
    

    泛型

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

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

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

    function getData(): Result {
     const data: any = [
     {id: 1, name: '类型注解'},
     {id: 2, name: '类型推论'},
     {id: 3, name: '编译型语言'} 
     ];
     return {data};
    }
    
    // 调用
    this.features = getData().data;
    
    

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

    function getData(): Promise> {
     const data: any = [
     {id: 1, name: '类型注解'},
     {id: 2, name: '类型推论'},
     {id: 3, name: '编译型语言'} 
     ];
     return Promise.resolve>({data});
    }
    
    // 调用 async 方式
    async mounted () {
     this.features = (await getData()).data;
    }
    
    // 调用 then 方式
    mouted () {
     getData().then((res: Result) => {
     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

    
    
    

    源码简单了解

    类装饰器主要依赖库: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 获取设置在类装饰器上的元数据。可参考文章理解:

    • Decorators
    • TypeScript:理解 Reflect Metadata
    • JavaScript Reflect Metadata 详解

    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: '',
      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 库地址

    
    
    
    
    
    
    

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

    
    
    
    
    

    其他

    修改本地开发端口号,在 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
      }])
     }
     }
    }
    
    

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持创新互联。


    分享名称:如何用vue-cli3脚手架搭建一个基于ts的基础脚手架的方法
    本文路径:https://www.tyhkzb.com/article/jddspe.html
    在线咨询
    服务热线
    服务热线:028-86922220
    TOP