头图

【2023】uniapp+vue3+ts超实用模板

构建超实用的 uniapp+vue3+ts 模板,从此编码体验和用户体验爽得飞起。

0、前置说明

本模板基于cli生成,全程时候用VSCode编码,增加类型提示,开发体验很好。

  • 使用了图片压缩,再也不用先去外面压缩图片再重新上传了。
  • 使用了 unocss + unoIcons,再也不用从外面找图片/图标再引入了。
  • 使用了 svg-loadersvg 随便使用。
  • 开发环境: node >=18,pnpm >=8.10.2 。
生成项目:npx degit dcloudio/uni-preset-vue#vite-ts vue3-uniapp-template

1、引入 prerttier + eslint + stylelint

1-1 .editorconfig file

# .editorconfig 文件
root = true

[*] # 表示所有文件适用
charset = utf-8 # 设置文件字符集为 utf-8
indent_style = space # 缩进风格(tab | space)
indent_size = 2 # 缩进大小
end_of_line = lf # 控制换行类型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行首的任意空白字符
insert_final_newline = true # 始终在文件末尾插入一个新行

[*.md] # 表示仅 md 文件适用以下规则
max_line_length = off # 关闭最大行长度限制
trim_trailing_whitespace = false # 关闭末尾空格修剪

1-2 .prettierrc.cjs file

// .prettierrc.cjs 文件
// @see https://prettier.io/docs/en/options
module.exports = {
  singleQuote: true,
  printWidth: 100,
  tabWidth: 2,
  useTabs: false,
  semi: false,
  trailingComma: 'all',
  endOfLine: 'auto',
}

1-3 .eslintrc.cjs file

// .eslintrc.cjs 文件
module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:vue/vue3-essential',
    // eslint-plugin-import 插件, @see https://www.npmjs.com/package/eslint-plugin-import
    'plugin:import/recommended',
    // eslint-config-airbnb-base 插件, tips: 本插件也可以替换成 eslint-config-standard
    'airbnb-base',
    // 1. 接入 prettier 的规则
    'prettier',
    'plugin:prettier/recommended',
    'vue-global-api',
  ],
  overrides: [
    {
      env: {
        node: true,
      },
      files: ['.eslintrc.{js,cjs}'],
      parserOptions: {
        sourceType: 'script',
      },
    },
  ],
  parserOptions: {
    ecmaVersion: 'latest',
    parser: '@typescript-eslint/parser',
    sourceType: 'module',
  },
  plugins: [
    '@typescript-eslint',
    'vue',
    // 2. 加入 prettier 的 eslint 插件
    'prettier',
    // eslint-import-resolver-typescript 插件,@see https://www.npmjs.com/package/eslint-import-resolver-typescript
    'import',
  ],
  rules: {
    // 3. 注意要加上这一句,开启 prettier 自动修复的功能
    'prettier/prettier': 'error',
    // turn on errors for missing imports
    'import/no-unresolved': 'off',
    // 对后缀的检测,否则 import 一个ts文件也会报错,需要手动添加'.ts', 增加了下面的配置后就不用了
    'import/extensions': [
      'error',
      'ignorePackages',
      { js: 'never', jsx: 'never', ts: 'never', tsx: 'never' },
    ],
    // 只允许1个默认导出,关闭,否则不能随意export xxx
    'import/prefer-default-export': ['off'],
    'no-console': ['off'],
    // 'no-unused-vars': ['off'],
    // '@typescript-eslint/no-unused-vars': ['off'],
    // 解决vite.config.ts报错问题
    'import/no-extraneous-dependencies': 'off',
    'no-plusplus': 'off',
    'no-shadow': 'off',
  },
  // eslint-import-resolver-typescript 插件,@see https://www.npmjs.com/package/eslint-import-resolver-typescript
  settings: {
    'import/parsers': {
      '@typescript-eslint/parser': ['.ts', '.tsx'],
    },
    'import/resolver': {
      typescript: {},
    },
  },
}

并安装相关的依赖包:

pnpm add -D eslint eslint-config-airbnb-base eslint-config-prettier eslint-import-resolver-typescript eslint-plugin-import eslint-plugin-prettier eslint-plugin-vue @typescript-eslint/eslint-plugin @typescript-eslint/parser vue-global-api 

1-4 .stylelintrc.cjs file

// .stylelintrc.cjs 文件

module.exports = {
  root: true,
  extends: [
    'stylelint-config-standard',
    'stylelint-config-standard-scss', // tips: 本插件也可以替换成 stylelint-config-recommended-scss
    'stylelint-config-recommended-vue/scss',
    'stylelint-config-html/vue',
    'stylelint-config-recess-order',
  ],
  overrides: [
    // 扫描 .vue/html 文件中的<style>标签内的样式
    {
      files: ['**/*.{vue,html}'],
      customSyntax: 'postcss-html',
    },
    {
      files: ['**/*.{css,scss}'],
      customSyntax: 'postcss-scss',
    },
  ],
  // 自定义规则
  rules: {
    // 允许 global 、export 、v-deep等伪类
    'selector-pseudo-class-no-unknown': [
      true,
      {
        ignorePseudoClasses: ['global', 'export', 'v-deep', 'deep'],
      },
    ],
    'unit-no-unknown': [
      true,
      {
        ignoreUnits: ['rpx'],
      },
    ],
  },
}

并安装相关的依赖包:

pnpm add -D stylelint stylelint-config-html stylelint-config-recess-order stylelint-config-recommended-vue stylelint-config-standard stylelint-config-standard-scss postcss postcss-html postcss-scss sass

2、引入 husky + lint-staged + commitlint

2-1 先安装依赖:

pnpm i -D husky lint-staged commitlint @commitlint/cli @commitlint/config-conventional

2-2 再执行:npx husky install,并且在 package.json的scripts里面增加 "prepare": "husky install",(其他人安装后会自动执行) 根目录会生成 .hushy 文件夹。

package.josn 增加如下属性:

"lint-staged": {
  "**/*.{html,vue,ts,cjs,json,md}": [
    "prettier --write"
  ],
  "**/*.{vue,js,ts,jsx,tsx}": [
    "eslint --fix"
  ],
  "**/*.{vue,css,scss,html}": [
    "stylelint --fix"
  ]
},

2-3 根目录新增 commitlint.config.cjs,写内容如下:

// commitlint.config.cjs
module.exports = {
  extends: ['@commitlint/config-conventional'],
}

2-4 husky通过一下命令增加个文件:

npx husky add .husky/pre-commit "npx --no-install -- lint-staged"
npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"

以后提交到暂存区的文件会自动格式化并修复,很好用,同时提交规范也有限制。

3、vite.config.ts 优化

修改index.html(可选),在html标签增加:build-date="%BUILD_DATE%",以后构建完就能看到构建时间了。
修改vite.config.ts,把内容改为如下:

import path from 'node:path'
import { defineConfig, loadEnv } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import dayjs from 'dayjs'
import vue from '@vitejs/plugin-vue'
import svgLoader from 'vite-svg-loader'
import { visualizer } from 'rollup-plugin-visualizer'
import ViteRestart from 'vite-plugin-restart'
import Components from 'unplugin-vue-components/vite'
// ElementPlusResolver,
// AntDesignVueResolver,
// VantResolver,
// HeadlessUiResolver,
// ElementUiResolver
import {} from 'unplugin-vue-components/resolvers'
import AutoImport from 'unplugin-auto-import/vite'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import viteCompression from 'vite-plugin-compression'
import viteImagemin from 'vite-plugin-imagemin'
import vueSetupExtend from 'vite-plugin-vue-setup-extend'
import UnoCSS from 'unocss/vite'
import autoprefixer from 'autoprefixer'

const htmlPlugin = () => {
  return {
    name: 'html-transform',
    transformIndexHtml(html) {
      return html.replace('%BUILD_DATE%', dayjs().format('YYYY-MM-DD HH:mm:ss'))
    },
  }
}

// https://vitejs.dev/config/
export default ({ mode }) => {
  // mode: 区分生产环境还是开发环境
  // process.cwd(): 获取当前文件的目录跟地址
  // loadEnv(): 返回当前环境env文件中额外定义的变量
  const env = loadEnv(mode, path.resolve(process.cwd(), 'env'))
  console.log(env)
  return defineConfig({
    plugins: [
      uni(),
      UnoCSS(),
      htmlPlugin(),
      svgLoader(),
      // 打包分析插件
      visualizer(),
      ViteRestart({
        // 通过这个插件,在修改vite.config.js文件则不需要重新运行也生效配置
        restart: ['vite.config.js'],
      }),
      vueSetupExtend(),
      // 原先引用组件的时候需要在目标文件里面import相关组件,现在就可以直接使用无需在目标文件import了
      Components({
        dirs: ['src/components'], // 目标文件夹
        extensions: ['vue'], // 文件类型
        dts: 'src/components.d.ts', // 输出文件,里面都是一些import的组件键值对
        // ui库解析器,也可以自定义,需要安装相关UI库
        resolvers: [
          // VantResolver(),
          // ElementPlusResolver(),
          // AntDesignVueResolver(),
          // HeadlessUiResolver(),
          // ElementUiResolver()
        ],
      }),
      AutoImport({
        imports: ['vue'],
        dts: 'src/auto-import.d.ts',
      }),
      createSvgIconsPlugin({
        // 指定要缓存的文件夹
        iconDirs: [path.resolve(process.cwd(), 'src/assets/svg')],
        // 指定symbolId格式
        symbolId: 'icon-[dir]-[name]',
      }),
      viteCompression(), // 会多出一些.gz文件,如xxx.js.gz,这里默认是不会删除xxx.js文件的,如果想删除也可以增加配置
      // 这个图片压缩插件比较耗时,希望仅在生产环境使用
      viteImagemin({
        gifsicle: {
          // gif图片压缩
          optimizationLevel: 3, // 选择1到3之间的优化级别
          interlaced: false, // 隔行扫描gif进行渐进式渲染
          // colors: 2 // 将每个输出GIF中不同颜色的数量减少到num或更少。数字必须介于2和256之间。
        },
        optipng: {
          // png
          optimizationLevel: 7, // 选择0到7之间的优化级别
        },
        mozjpeg: {
          // jpeg
          quality: 20, // 压缩质量,范围从0(最差)到100(最佳)。
        },
        pngquant: {
          // png
          quality: [0.8, 0.9], // Min和max是介于0(最差)到1(最佳)之间的数字,类似于JPEG。达到或超过最高质量所需的最少量的颜色。如果转换导致质量低于最低质量,图像将不会被保存。
          speed: 4, // 压缩速度,1(强力)到11(最快)
        },
        svgo: {
          // svg压缩
          plugins: [
            {
              name: 'removeViewBox',
            },
            {
              name: 'removeEmptyAttrs',
              active: false,
            },
          ],
        },
      }),
    ],
    css: {
      postcss: {
        plugins: [
          autoprefixer({
            // 指定目标浏览器
            overrideBrowserslist: ['> 1%', 'last 2 versions'],
          }),
        ],
      },
    },

    resolve: {
      alias: {
        '@': path.join(process.cwd(), './src'),
      },
    },
    server: {
      host: '0.0.0.0',
      hmr: true,
      port: 7001,
      // 自定义代理规则
      proxy: {
        // 选项写法
        '/api': {
          target: 'http://localhost:6666',
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, ''),
        },
      },
    },
  })
}

安装相关依赖:

pnpm i -S dayjs
pnpm i -D vite-svg-loader rollup-plugin-visualizer vite-plugin-restart unplugin-vue-components unplugin-auto-import vite-plugin-svg-icons vite-plugin-compression vite-plugin-imagemin vite-plugin-vue-setup-extend unocss autoprefixer

新增 uno.config.ts,写入如下内容:

// uno.config.ts
import {
  defineConfig,
  presetAttributify,
  presetUno,
  presetIcons,
  transformerDirectives,
  transformerVariantGroup,
} from 'unocss'

export default defineConfig({
  presets: [
    presetUno(),
    // 支持css class属性化,eg: `<button bg="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600" text="sm white">attributify Button</button>`
    presetAttributify(),
    // 支持图标,需要搭配图标库,eg: @iconify-json/carbon, 使用 `<button class="i-carbon-sun dark:i-carbon-moon" />`
    presetIcons({
      scale: 1.2,
      warn: true,
      extraProperties: {
        display: 'inline-block',
        'vertical-align': 'middle',
      },
    }),
  ],
  transformers: [
    transformerDirectives(),
    // 支持css class组合,eg: `<div class="hover:(bg-gray-400 font-medium) font-(light mono)">测试 unocss</div>`
    transformerVariantGroup(),
  ],
})

main.ts增加如下2行:

import 'virtual:svg-icons-register'
import 'virtual:uno.css'

4、VSCode插件推荐及uniapp类型提示

4-1 VSCode插件推荐如下几个

  • Vue Language Features (Volar) 插件: vue.volar
  • TypeScript Vue Plugin (Volar)插件: vue.vscode-typescript-vue-plugin
  • uni-create-view插件: mrmaoddxxaa.create-uniapp-view
  • uni-app-schemas插件: uni-helper.uni-app-schemas-vscode
  • uni-helper插件: uni-helper.uni-helper-vscode
  • uniapp小程序扩展:evils.uniapp-vscode
  • SCSS IntelliSense插件: mrmlnc.vscode-scss
  • UnoCss插件: antfu.unocss

4-2 uniapp 类型提示依赖包

pnpm i -D @types/wechat-miniprogram @uni-helper/uni-app-types

并把 tsconfig.json 修改如下:

{
  "compilerOptions": {
    "sourceMap": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    },
    "lib": ["esnext", "dom"],
-    "types": ["@dcloudio/types"]
+    "types": ["@dcloudio/types", "@types/wechat-miniprogram", "@uni-helper/uni-app-types"]
  },
+  "vueCompilerOptions": {
+    "nativeTags": ["block", "template", "component", "slot"]
+  },
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

现在写代码 uni.xxx 就会有提示了,但是 uni 本身会报eslint错误,提示(no-undef),这个只需要在.eslintrc.cjs里面增加配置就行:

{
    // ... 原本的配置
+    globals: {
+      uni: true,
+    },
}

这里只要加 uni,不需要加 wx等其他API,因为我们会用 uni.xxx代替wx.xxx 提高兼容性,以后多端不用改代码,如果有兼容性问题,可以使用条件编译。

5、pinia + pinia-plugin-persistedstate 插件

首先安装依赖包:

pinia i -S pinia pinia-plugin-persistedstate

然后写入文件

// src/store/useCountStore.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'

export const useCountStore = defineStore(
  'count',
  () => {
    const count = ref(0)
    const increment = () => {
      count.value++
    }
    return {
      count,
      increment,
    }
  },
  {
    persist: true,
  },
)

注意下面这个文件对持久化的处理,否则非h5环境不能正确持久化。

// src/store/index.ts
import { createPinia } from 'pinia'
import { createPersistedState } from 'pinia-plugin-persistedstate' // 数据持久化

const store = createPinia()
store.use(
  createPersistedState({
    storage: {
      getItem: uni.getStorageSync,
      setItem: uni.setStorageSync,
    },
  }),
)
export default store
// src.main.ts
import { createSSRApp } from 'vue'
import App from './App.vue'
+ import store from './store'
import 'virtual:svg-icons-register'
import 'virtual:uno.css'

export function createApp() {
  const app = createSSRApp(App)
+ app.use(store)
  return {
    app,
  }
}

使用如下(/src/pages/index/index.vue):

// script
import { useCountStore } from '@/store/count'

const countStore = useCountStore()
<!-- template -->
<view class="flex text-red-500">
  Demo Count: {{ countStore.count }}
  <button @click="countStore.increment">新增</button>
</view>

可以在小程序看到存储的值,如下图所示。

pinia-persistedstate

今天先写到这,其他的下周再写。

本文由mdnice多平台发布


斐鸽传书
7 声望0 粉丝