Vite

Vite is an opinionated web dev build tool that serves your code via native ES Module imports during dev and bundles it with Rollup for production.

目前,Vue-cli 还未能直接搭建 vue3 应用,需要在 vue2 的项目上通过执行vue add next命令进行升级。

而 Vite 是官方提供的一个可以快速搭建 vue3 新工具,它是一个简易的 http 服务器,无需通过 webpack 打包即可实时解析 vue3 文件,并能实现热更新。

现在最新版的 vite 已经提供了一个简易模板,可以直接使用一下命令快速搭建

安装与启动

$ npx create-vite-app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev

具体参考官方仓库: https://github.com/vuejs/vite#getting-started

Typescript

vite 最新版默认已经可以直接对 typescript 进行解析编译,直接在 vue 文件中的script标签下加入lang="ts"即可。

Sass

执行命令安装 sass, npm i sass -D, 然后在 vue 文件的style标签下加入lang="scss"即可,这些与以前 vue2 都是一样的。

Vuex

支持 vue3 的 vuex 最新版现在还未正式发版,需要使用npm i vuex@next -D来安装。
Vuex 的新语法进行了一些变更,你需要使用 Vuex.createStore 来创建,其他语法没有区别。

import Vuex from "vuex";
// or => import { createStore } from 'vuex'
export default Vuex.createStore({
  state: {},
  mutations: {},
  actions: {},
  modules: {},
});

然后在 main.js 引入这个文件并 use

import { createApp } from "vue";
import App from "./App.vue";
import store from "./store/index.js";
import "./assets/base.css";
createApp(App).use(store).mount("#app");

Vue Compostion Api

Vue3 最大的一个变化就是使用Vue Compostion Api, 现在官方已经提供了 中文文档

本次只用到了其中很少一部分的 API,而且可能使用方式不一定准确,仅供参考,望见谅。

功能设计

  • 对 TodoList 的操作:完成、取消、添加、删除
  • 可记录不同日期的 TodoList
  • 对记录了 TodoList 的日期进行标注

现在先来看一下最终项目 DEMO 成果

点击头部的日期会弹出日期选择,可选择其他日期。

实现

主要组件Card.vue组件,然后里面包含一个日期选择组件DatePicker.vue 为了练习一下 vuex 在 vue3 的使用,将当前的选择日期记录在了 vuex 中。

主要逻辑代码

// Card.vue
import { ref, reactive, computed, watch } from "vue";
import { useStore } from "vuex";
import { Todo, getTodoList, setTodoList } from "../model/todo";
import DatePicker from "./DatePicker.vue";
const weekArr: string[] = [
  "Sunday",
  "Monday",
  "Tuesday",
  "Wednesday",
  "Thursday",
  "Firday",
  "Saturday",
];
export default {
  name: "Card",
  components: {
    DatePicker,
  },
  directives: {
    focus(el) {
      el.focus();
    },
  },
  props: {
    date: String,
    showAddBtn: Boolean,
  },
  setup(props) {
    const store = useStore();
    const state = {
      editingValue: ref(""),
      showDatePicker: ref(false),
    };
    let todoList = reactive([]);
    watch(
      () => props.date,
      (val) => {
        todoList.length = 0;
        todoList.push(...getTodoList(props.date));
        // state.showDatePicker.value = false
      },
      {
        immediate: true,
      }
    );
    const weekDay = computed(() => weekArr[new Date(props.date).getDay()]);
    const formatterDate = computed((): string => {
      const arr: string[] = new Date(props.date).toDateString().split(" ");
      return `${arr[1]} ${arr[2]}, ${arr[3]}`;
    });
    const handleChecked = (item: Todo): void => {
      item.isChecked = !item.isChecked;
      setTodoList(props.date, todoList);
      store.commit("refreshTodoListDateArr");
    };
    const handleRemove = (index: number): void => {
      todoList.splice(index, 1);
      setTodoList(props.date, todoList);
      store.commit("refreshTodoListDateArr");
    };
    const handleAdd = (): void => {
      state.editingValue.value = "";
      todoList.push({
        content: "",
        isChecked: false,
        isEditing: true,
      });
    };
    const handleEditSubmit = (item: Todo, index: number): void => {
      if (item.isEditing) {
        if (state.editingValue.value) {
          item.content = state.editingValue.value;
          item.isEditing = false;
        } else {
          todoList.splice(index, 1);
        }
        setTodoList(props.date, todoList);
        store.commit("refreshTodoListDateArr");
      }
    };
    return {
      weekDay,
      formatterDate,
      todoList,
      handleChecked,
      handleRemove,
      handleAdd,
      handleEditSubmit,
      ...state,
    };
  },
};

组件功能实现都比较简单,本次只为了熟悉语法,所以具体代码逻辑在这就不进行讲解了。

项目打包

使用npm run build或者npx vite build即可打包项目。

Vite 的打包基于 Rollup

需要注意的是,vite 也是想 vue-cli 那样提供了配置文件的,在根目录下添加vite.config.js即可,例如本地打包后项目是通过 https://kongfandong.cn/todo 来访问的,如果使用默认的打包配置,会出现资源 404 的问题。这时需要给打包配置资源路径/todo,加入以下配置:

// vite.config.js
module.exports = {
  base: "/todo",
};

Vite 还提供了很多其他的配置,具体请参考: https://github.com/vuejs/vite/blob/master/src/node/config.ts

一个插曲

一开始,我使用 vuex 是使用getCurrentInstance获取 vue 的实例想像 vue2 那样类似this.$store.state来使用 vuex 的,就像下面的代码

import { computed, getCurrentInstance } from "vue";
export default {
  setup() {
    const { ctx } = getCuurentInstance();
    const selectedDate = computed(() => ctx.$store.state.selectedDate);
    return {
      selectedDate,
    };
  },
};

这样 ts 会报错,提示 ctx 的类型没找到,通过查源码发现 vue 并没有把 ctx 的类型抛出。然后我就改成了

import { computed, getCurrentInstance } from "vue";
export default {
  setup() {
    const instance: any = getCuurentInstance();
    const ctx = instance.ctx;
    const selectedDate = computed(() => ctx.$store.state.selectedDate);
    return {
      selectedDate,
    };
  },
};

然后一切如愿进行,开发环境下什么问题也没有了。但是,到了打包之后运行,浏览器就报错了

index.js:9 Uncaught TypeError: Cannot read property 'state' of undefined
    at index.js:9
    at n (index.js:1)
    at Object.get value [as value] (index.js:1)
    at Object.get (index.js:1)
    at Object.get (index.js:1)
    at index.js:9
    at Vn (index.js:1)
    at Proxy.<anonymous> (index.js:9)
    at Proxy.<anonymous> (index.js:1)
    at nt (index.js:1)
(anonymous) @ index.js:9
...

报错 state 没找到,程序上的 vuex 的$store 是 undefined。然后我查了好久也没找到解决办法,于是只能在 github 上提个 issue。

最后得到了解答,原来在setup()中是不能使用getCurrentInstance的!

具体 Issue: https://github.com/vuejs/vite/issues/156

最终的解决办法,引入 vuex 的 useStore 方法,就可以在 setup 中使用 vuex 了。

最后代码改为:

import { computed } from "vue";
import { useStore } from "vuex";
export default {
  setup() {
    const store = useStore();
    const selectedDate = computed(() => ctx.$store.state.selectedDate);
    return {
      selectedDate,
    };
  },
};

总结

这个小 Demo 的实现过来还是遇到了不少问题,但还是一一解决了,可以让我对 vue3 与 typescript 进行了初步的了解。目前只是因为还不太熟悉,但感觉 vue3 未来发展潜力还是很大的。

还有 Vite 这个工具好像也是很强大的,目测 Vue3 官方后面有可能会直接推荐使用该工具进行开发了,有可能会放弃的 Webpack 了。