Building Vue3 + Typescript + without cli 最佳实践

92 阅读2分钟

随着Vue3正式版的到来,带来了一些新的特性:

  • Composition API: 这是一个从react hooks上受启发的组件
  • Portals: 可以在vue当前组件外渲染dom节点
  • Fragments: 允许有多个root节点
  • 更新的v-model-api: 支持多个models
  • Suspense: 异步加载组件的loading界面
  • TypeScript: Vue从现在起完全支持Typescript

本着学习的精神,我决定尝试下从零开始自己搭建一个Vue3 + Typescript的项目

创建一个项目

第一件事就是初始化一个项目

yarn init

然后添加依赖,对Vue3实现最基本的支持

yarn add vue
yarn add -D webpack webpack-cli webpack-dev-server @vue/compiler-sfc

添加webpack配置

import { resolve } from './utils'
import { Configuration as WebpackConfiguration, HotModuleReplacementPlugin } from "webpack"
import { Configuration as WebpackDevServerConfiguration } from "webpack-dev-server"
import HtmlWebpackPlugin from "html-webpack-plugin"
import { VueLoaderPlugin } from 'vue-loader'

interface Configuration extends WebpackConfiguration {
  devServer?: WebpackDevServerConfiguration;
}

const config: Configuration = {
  entry: "./src/main.tsx",
  resolve: {
    extensions: [".tsx", ".ts", ".js", ".json"],
    alias: {
      vue$: 'vue/dist/vue.runtime.esm-bundler.js',
      '@': resolve('src')
    }
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
      },
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        loader: 'ts-loader',
        options: {
          appendTsSuffixTo: [/\.vue$/]
        }
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
  },
  plugins: [
    new VueLoaderPlugin()
  ],
  devServer: {
    historyApiFallback: true,
    host: 'localhost',
    port: 4000,
    open: true,
    hot: true,
    allowedHosts: 'all',
    compress: true, // 为所有服务启用gzip 压缩
  }
}

export default config

增加Typescript支持

方案一:使用ts-loader

yarn add -D ts-loader
module: {
  rules: [
    {
      test: /\.tsx?$/,
      exclude: /node_modules/,
      loader: 'ts-loader',
      options: {
        appendTsSuffixTo: [/\.vue$/]
      }
    }
  ]
}

方案二:使用babel-loader + @babel/preset-typescript

yarn add -D @babel/core babel-loader @babel/preset-typescript
module: {
  rules: [
    {
      test: /\.(jsx?|tsx?)$/,
      exclude: /node_modules/,
      use: 'babel-loader'
    }
  ]
}
  • babel.config.js
module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        useBuiltIns: 'usage',
        corejs: '3.0',
      },
    ],
    [
      "@babel/preset-typescript",
      {
        allExtensions: true, // 支持所有文件扩展名,否则在vue文件中使用ts会报错
      },
    ]
  ]
}

tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "useDefineForClassFields": true,
    "sourceMap": true,
    "baseUrl": "./",
    "typeRoots": [
      "types",
      "node_modules/@types"
    ],
    "paths": {
      "@/*": ["src/*"]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "ts-node": {
    "compilerOptions": {
      "module": "CommonJS"
    }
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

配置package.json命令

当webpack基本配置好后,添加scripts命令来运行

{
  "scripts": {
    "dev": "webpack serve --development",
    "build": "webpack --mode production"
  }
}

.vue文件使用ts问题

为了避免.vue文件使用ts报错,在src目录下添加shim-vue.d.ts文件

declare module '*.vue' {
  import { defineComponent } from 'vue'
  const component: ReturnType<typeof defineComponent>
  export default component
}

vue-router

vue3相对应的vue-router版本是@4.0+的,语法上与过往版本略有区别

  • 创建Vue-router配置,createWebHashHistory为hash模式,createWebHistory为history模式
import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '@/Home.vue'

const routes = [
  {
    path: '/',
    component: Home
  }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

export default router

pinia

pinia是完全使用Composition API编写的,对于使用新的特性非常适合,可以抛弃vuex了。

import { defineStore } from 'pinia'

export const useStore = defineStore('main', {
  // other options...
  state: () => ({
    counter: 0
  }),
  actions: {
    increment() {
      this.counter++
    }
  }
})

Vue入口文件main.ts

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
const app = createApp(App) // 创建Vue实例
const pinia = createPinia() // 创建pinia实例
app.use(router)
app.use(pinia)
app.mount('#app')

结语

以上就是分享的使用Vue + TypeScript的简单案例,欢迎互相交流。