vue2.0 + ts项目使用心得

2,833 阅读4分钟

vue2.0 + ts项目使用心得

安装命令

npm install -g @vue/cli
# OR
yarn global add @vue/cli

然后我们就可以通过vue create XXX来创建我们得 vue 项目

我们选中图中几项常用得就可以创建出我们得 vue 项目

vue-cli3 脚手架生成项目目录说明

│  .browserslistrc
│  .gitignore
│  .postcssrc.js // postcss 配置
│  babel.config.js
│  package.json // 依赖
│  README.md // 项目 readme
│  tsconfig.json // ts 配置
│  tslint.json // tslint 配置
│  vue.config.js // webpack 配置(~自己新建~)
│  yarn.lock
│
├─public // 静态页面
│  │—favicon.ico
│  │—index.html
│
├─src // 主目录
│  ├─assets // 静态资源
│  │      logo.png
│  │
│  ├─components
│  │      HelloWorld.vue
│  │
│  │─views // 页面
│  │      About.vue
│  │      Home.vue
│  │
│  │  App.vue // 页面主入口
│  │
│  │  main.ts // 脚本主入口
│  │
│  ├─router // 路由配置
│  │      index.ts
│  │
│  │  registerServiceWorker.ts // PWA 配置
│  │
│  │  shims-tsx.d.ts // jsx语法的声明
│  │  shims-vue.d.ts // vue的声明
│  │  shims-global.d.ts  // 声明一些全局模块或第三方插件(自己创建)
│  │  shims-mount-vue.d.ts // 声明挂载在vue上的模块(自己创建)
│  │
│  │
│  ├─store // vuex 配置
│  │      index.ts
│  │
│  ├─typings // 全局注入(~自己新建~)
│  │
│  ├─utils // 工具方法(axios封装,全局方法等)(~自己新建~)
│  │      index.ts
│  │
│  ├─system // 配置系统方法及组件
│  │      Initialization.ts 初始化基础配置 在main.ts中使用


shims-vue.d.ts 文件说明

由于 TypeScript 默认并不支持 .vue 后缀的文件,所以在 vue 项目中引入的时候需要创建一个 shims-vue.d.ts 文件,放在项目项目对应使用目录下,例如 src/shims-vue.d.ts,用来支持.vue 后缀的文件;

shims-mount-vue.d.ts 文件说明

声明挂载在 vue 上得一些组件及方法 比如:

import { Route } from 'vue-router';

declare module 'vue/types/vue' {
  interface Vue {
    $router: VueRouter; // 这表示this下有这个东西
    $route: Route;
    $http: http //封装得http方法;
    $_toast: any; // 自己封装得组件,可以直接通过this调用
    commonStr: string; // 全局使用得动态数据 比如动态图片链接等
  }

vue 中 router 编写

vue 中路由编写比较简单我们只需导入 vue-router

import Vue from 'vue';
import Router from 'vue-router';

Vue.use(Router);

export default new Router({
  mode: 'hash',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/home',
      name: 'home',
      component: () => import(/* webpackChunkName: "h5Webview" */ '../pages/index.vue'),
      meta:{
         keepAlive:true
      } //这个地方编写一些自定义得属性 比如keepAlive以及路由鉴权等
    },
  ]
});

// 使用需要在跟文件使用
    <template>
        <keep-alive :include="includeComponents">
          <router-view class="view-transition"></router-view>
        </keep-alive>
      </template>
    </transition>

vue 中组件和方法得封装

vue 中组件得封装和 React 是不同得 Vue 组件分为全局注册和局部注册,在 react 中都是通过 import 相应组件,然后模版中引用 props 是可以动态变化的,子组件也实时更新,在 react 中官方建议 props 要像纯函数那样,输入输出一致对应,而且不太建议通过 props 来更改视图

子组件一般要显示地调用 props 选项来声明它期待获得的数据。而在 react 中不必需,另两者都有 props 校验机制

每个 Vue 实例都实现了事件接口,方便父子组件通信,小型项目中不需要引入状态管理机制,而 react 必需自己实现

使用插槽分发内容,使得可以混合父组件的内容与子组件自己的模板

多了指令系统,让模版可以实现更丰富的功能,而 React 只能使用 JSX 语法

Vue 增加的语法糖 computed 和 watch,而在 React 中需要自己写一套逻辑来实现 那我们现在简单封装一个 vue 组件

// 在ts中vue一些vue方法建议使用装饰器来修饰 @Component({}) 一种依赖注入的方法,为了降低代码的耦合度引用组件在这个里面进行 @prop()--> props接受数据 private:私有数据类似state
// get computed计算方法 @Emit 组件传值使用
<template>
    <button :class="clsStyle" :disabled="disabled" @click="clickHandler">
        <span :style="{color:this.color}"><slot></slot></span>
    </button>
</template>

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

@Component({
  name: 'common-button',
})
export default class CommonButton extends Vue {
  @Prop({default: ''}) type?: String;
  @Prop({default: ''}) shape?: String;
  @Prop({default: ''}) icon?: String;
  @Prop({default: false}) disabled?: Boolean;
  @Prop({default: false}) block?: Boolean;
  @Prop({default: false}) small?: Boolean;
  @Prop({default: false}) label?: Boolean;
  @Prop({default: ''}) color?: String;

  get clsStyle() {
    let cls = 'common-button ';
    cls += `${this.disabled ? '' : this.type} ${this.shape}`;
    cls += this.small ? ' small' : '';
    cls += this.block ? ' block' : '';
    cls += this.label ? ' label' : '';
    if(!this.$slots.default) {
      if(this.small) {
        cls += ' no-txt-small';
      }else {
        cls += ' no-txt';
      }
    }
    return cls;
  }

  @Emit('click')
  clickHandler() {}
}
</script>

那我们想使用这个组件就可以通过 import 使用了 但有人感觉每次都要 import 很麻烦 我们就可以把他挂载到 vue 全局上

import { VueConstructor } from "vue";
import Button from "./button.vue";
import "./button.scss";

Button.install = function(Vue: VueConstructor) {
  Vue.component(Button.name, Button);
};

export default Button;

然后在我们 main.ts 中 Vue.use(Button)j 就可以了 这样就可以全局使用 但是有些情况,使用提示类得组件我们想挂载在 this 上使用会更方便,这样得话我们就需要单独处理了比如我们常用得继续以我们 button 组件为例

// _button.ts
import Vue from 'vue';
import settings from './button.vue';

interface iInst extends Vue {
  vm: Vue;
  visible: boolean;
}
let ButtonConstructor = Vue.extend(settings); //集成button组件

let inst: iInst;

let Button = function (options: {
  id: string;
}) {
  options.id = options.id || 'common-button-default-id';

  inst = new ButtonConstructor({
    propsData: Object.assign(options, { openFromMethod: true })
  });

  inst.vm = inst.$mount();

  let buttonDom: HTMLElement | null = document.querySelector('#' + options.id);
  if (options.id && buttonDom && buttonDom.parentNode) {
    buttonDom.parentNode.replaceChild(inst.$el, buttonDom);
  } else {
    document.body.appendChild(inst.$el);
  }

  Vue.nextTick(() => {
    inst.visible = true;
  });
};

export default Button;

```然后只需要修改上面得函数
```js
import { VueConstructor } from 'vue';
import ButtonVue from './button.vue';
import Button from './_button';
import './button.scss';
const ButtonArr: any = [Button, ButtonVue];
ButtonArr.install = function(Vue: VueConstructor) {
  Vue.prototype['$_Button'] = Select;
  Vue.component(Button.name, Button);
};

export default Button;

这样我们就能通过 this.$_button 来使用这个组件 但是这中用法比较适合那些弹出类组件 这儿只是做个例子 以上就是 vue 项目搭建得大致流程 目前 vue2.0 对 ts 的支持并不友好建议 3.0 后升级 ts

vue3.0 初体验

3.0 目前了解的不太多,就只针对 2.0 的一些体验不好的地方看了一下

目前 Vue2 的代码模式下存在的几个问题。

随着功能的增长,复杂组件的代码变得越来越难以维护。尤其发生你去新接手别人的代码时。根本原因是 Vue 的现有 API 通过「选项」组织代码,但是在大部分情况下,通过逻辑考虑来组织代码更有意义。

缺少一种比较「干净」的在多个组件之间提取和复用逻辑的机制。

类型推断不够友好。

针对以上举个简单的例子 在 Vue2 中如果我需要请求一份数据,并且在 loading 和 error 时都展示对应的视图,一般来说,我们会这样写:

<template>
    <div v-if="error">failed to load</div>
    <div v-else-if="loading">loading...</div>
    <div v-else>hello {{fullName}}!</div>
</template>

<script>
export default {
  data() {
    // 集中式的data定义 如果有其他逻辑相关的数据就很容易混乱
    return {
        data: {
            firstName: '',
            lastName: ''
        },
        loading: false,
        error: false,
    },
  },
  async created() {
      try {
        // 管理loading
        this.loading = true
        // 取数据
        const data = await this.$axios('/api/user')
        this.data = data
      } catch (e) {
        // 管理error
        this.error = true
      } finally {
        // 管理loading
        this.loading = false
      }
  },
  computed() {
      // 没人知道这个fullName和哪一部分的异步请求有关 和哪一部分的data有关 除非仔细阅读
      // 在组件大了以后更是如此
      fullName() {
          return this.data.firstName + this.data.lastName
      }
  }
}
</script>

这段代码,怎么样都谈不上优雅,凑合的把功能完成而已,并且对于 loading、error 等处理的可复用性为零。 在 3.0 中我们就可以这样写了

<template>
    <div v-if="error">failed to load</div>
    <div v-else-if="loading">loading...</div>
    <div v-else>hello {{fullName}}!</div>
</template>

<script>
import useSWR from 'vue-swr'
// 类似umi-hooks

export default createComponent({
// setup 函数,就相当于 vue 2.0 中的 created
  setup() {
      const { data, loading, error } = useSWR('/api/user', fetcher)
      // 计算属性
      const fullName = computed(() => data.firstName + data.lastName)
      return { data, fullName, loading, error }
  }
})
</script>

Vue3.0 函数式组件基本模板


<template>
  <div>
    <span>count is {{ count }}</span>
    <span>plusOne is {{ plusOne }}</span>
    <button @click="increment">加一</button>
  </div>
</template>

<script>
import { value, computed, watch, onMounted } from 'vue'

export default {
  setup(props) {
    // 初始数据
    const count = value(0)
    // 计算属性
    const plusOne = computed(() => count.value + 1)
    // 方法
    const increment = () => { count.value++ }
    // 每次count改变执行
    watch(() => count.value, val => {
      console.log(val)
    })
    // 生命周期
    onMounted(() => {
      console.log(`mounted`)
    })
    // expose bindings on render context
    return {
      count,
      plusOne,
      increment
    }
  }
}
</script>

在 vue3.0 中我们也可以想 react 编写 hooks 比如一个简单控制 boolean 的 hooks

import { useState, useCallback } from "react";

function useBoolean(defaultValue = false) {
  const [value, setValue] = useState(defaultValue);

  const toggle = useCallback(
    (value) => {
      setState(value);
    },
    [state]
  );

  return {
    value,
    toggle,
  };
}

export default useBoolean;

在 vue3.0 中我们可以这样写

import { value } from "vue";
function useBoolean(defaultValue = false) {
  const state = value(defaultValue);
  const toggle = (value) => {
    state.value = value;
  };
  return {
    state,
    toggle,
  };
}

export default useBoolean;

使用也并没有什么区别都是 const {state,toggle} = useBoolean(defalueValue)

语法区别 vue 中没有 useCallBack useMemo 等性能优化方案 具体原因不太清楚 感觉应该是使用 Proxy 的原因

要注意的是,vue3.0 中 返回的是一个对象,通过 .value 才能访问到其真实值 类似 react 里的 useRef,但是在模板中使用可以直接用

目前了解的就是 vue3.0 的这么多信息 感觉等正式版出来后应该也是不错的