夜听城嚣 夜听城嚣
首页
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 前端基建与架构
  • 专题分享

    • Git入门与开发
    • 前端面试题汇总
    • HTML和CSS知识点
  • 项目实践
  • 抓包工具
  • 知识管理
  • 工程部署
  • 团队规范
bug知多少
  • 少年歌行
  • 青年随笔
  • 文海泛舟
  • 此事躬行

    • 项目各工种是如何协作的
    • TBA课程学习
收藏

dwfrost

前端界的小学生
首页
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 前端基建与架构
  • 专题分享

    • Git入门与开发
    • 前端面试题汇总
    • HTML和CSS知识点
  • 项目实践
  • 抓包工具
  • 知识管理
  • 工程部署
  • 团队规范
bug知多少
  • 少年歌行
  • 青年随笔
  • 文海泛舟
  • 此事躬行

    • 项目各工种是如何协作的
    • TBA课程学习
收藏
  • 抓包工具

  • 知识管理

  • 编辑器工具

  • 工程部署

  • 团队规范

    • VSCode使用Eslint
    • husky+lint-staged+commitlint规范代码提交
    • uniapp小程序模板搭建
      • webpack3老项目升级为vite实践
      • webpack3项目升级webpack4实践
      • webpack4项目升级webpack5实践
      • 提交代码时修改commit消息
    • 效率工具
    • 团队规范
    frost
    2021-11-23

    uniapp小程序模板搭建

    # 写在前面

    2022-01-14更新:后来看到官方支持vite打包了,于是更改了模板。

    之所以想折腾下uniapp小程序模板,是因为现有小程序的架子版本很老了,项目是2年前的,基于uniapp框架,使用的模板是uni-preset-vue (opens new window)模板预设,整体依赖版本都偏低。本次模板搭建是想对uniapp模板进行升级并维护,基于团队规范搭建自己的模板。

    本工程基于uniapp的uni-preset-vue#vite (opens new window)模板,技术栈为vite2+vue3+vuex4+typescript,ui框架选用uni-ui。

    # 探索过程

    参考官方文档进行工程搭建。

    通过命令行创建uni-app (opens new window)

    uni-app 项目支持 vue 3.0 介绍,及升级指南 (opens new window)

    # 安装

    npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project  
    
    1

    # 使用sass

    npm i sass -D
    
    1

    不需要额外配置,直接使用scss即可。

    # eslint

    参考vue官方文档 (opens new window)进行配置。

    npm i eslint eslint-plugin-vue -D
    
    1
      "scripts": {
          "lint": "eslint --fix --ext .js,.vue src"
      }
    
    1
    2
    3
    // .eslintrc.js
    module.exports = {
      env: {
        browser: true,
        es6: true
      },
      extends: ['eslint:recommended', 'plugin:vue/vue3-essential'],
      globals: {
        Atomics: 'readonly',
        SharedArrayBuffer: 'readonly',
        uni: true,
        wx: true,
        require: true,
        process: true,
        getApp: true,
        getCurrentPages: true,
        gioGlobal: true,
        Component: true,
        requirePlugin: true,
        exports: true,
        define: true,
        global: true
      },
      parser: 'vue-eslint-parser',
      parserOptions: {
        parser: '@typescript-eslint/parser',
        ecmaVersion: 2020,
        sourceType: 'module'
      },
      plugins: ['vue'],
      rules: {
        'linebreak-style': ['off', 'windows'],
        // 单引号
        quotes: ['error', 'single'],
        // js部分不要分号
        semi: [0, 'always'],
        'no-undef': 'off',
        // Vue 风格
        // 指令缩写
        'vue/v-bind-style': ['error', 'shorthand'],
        'vue/v-on-style': ['error', 'shorthand'],
        // 组件/实例的选项的顺序
        'vue/order-in-components': [
          'error',
          {
            order: [
              'el',
              'name',
              'parent',
              'functional',
              ['delimiters', 'comments'],
              ['components', 'directives', 'filters'],
              'extends',
              'mixins',
              'inheritAttrs',
              'model',
              ['props', 'propsData'],
              'data',
              'computed',
              'watch',
              'LIFECYCLE_HOOKS',
              'onLoad',
              'onReady',
              'onShow',
              'onHide',
              'onUnload',
              'methods',
              ['template', 'render'],
              'renderError'
            ]
          }
        ],
        // 元素/组件特性,属性的顺序
        'vue/attributes-order': [
          'error',
          {
            order: [
              'DEFINITION', // e.g. 'is', 'v-is'
              'LIST_RENDERING', // e.g. 'v-for item in items'
              'CONDITIONALS', // e.g. 'v-if', 'v-else-if', 'v-else', 'v-show', 'v-cloak'
              'RENDER_MODIFIERS', // e.g. 'v-once', 'v-pre'
              'GLOBAL', // e.g. 'id'
              'UNIQUE', // e.g. 'ref', 'key'
              'SLOT', // e.g. 'v-slot', 'slot'.
              'TWO_WAY_BINDING', // e.g. 'v-model'
              'OTHER_DIRECTIVES', // e.g. 'v-custom-directive'
              'OTHER_ATTR', // e.g. 'custom-prop="foo"', 'v-bind:prop="foo"', ':prop="foo"'
              'EVENTS', // e.g. '@click="functionCall"', 'v-on="event"'
              'CONTENT' // e.g. 'v-text', 'v-html'
            ]
          }
        ]
      },
      overrides: [
        {
          files: ['*.vue'],
          rules: {
            indent: 'off'
          }
        }
      ]
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103

    注意:

    1. extends原本设置为plugin:vue/vue3-recommended,但实践后发现语法跟平时开发相差太大了,于是改为plugin:vue/vue3-essential,只保留必要的。
    2. 发现eslint与ts不兼容,于是新增了@typescript-eslint/parser。参考 (opens new window)。

    其他配置均来自原有团队规范。

    # vscode插件Volar

    此插件是vue官方推荐的,开发vue3必备。不过它与Vetur会冲突,可以在vue3的项目中禁用Vetur。

    # ts-配置@别名

    import { store } from '@/store'
    
    1

    会报ts错误:

    Cannot find module '@/http' or its corresponding type declarations.ts(2307)
    
    1

    需要在tsconfig.json中新增配置。

    	"paths": {
          "@/*": ["./src/*"]
        }
    
    1
    2
    3

    # 使用vuex4

    模板已安装了vuex4,也可以参考文档 (opens new window)。

    和之前的使用区别不大:

    // src/store/index.ts
    import { createStore } from 'vuex'
    import test from './modules/test'
    
    interface State {
      count: number
    }
    
    const state: State = {
      count: 1
    }
    
    const mutations = {
      increment(state: State, payload = 1) {
        state.count += payload
      }
    }
    
    const options = {
      state,
      mutations,
      modules: {
        test
      }
    }
    export const store = createStore(options)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    // main.ts
    import { createSSRApp } from 'vue'
    import App from './App.vue'
    import { store } from './store'
    
    export function createApp() {
      const app = createSSRApp(App)
      app.use(store)
      return {
        app
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    使用方式有2种

    // 该方式是官方推荐的,并且对 store.state.test.count 不会有类型报错
    import { useStore } from 'vuex'
    const store = useStore()
    
    // 该方式少一行代码,但会有类型报错
    // import { store } from '@/store'
    
    1
    2
    3
    4
    5
    6

    其他使用方式同vuex3,不过在setup模式下,没有辅助函数。

    const count = computed(() => store.state.count)
    const moduleCount = computed(() => store.state.test.count)
    
    const addCount = (type: string) => {
      console.log('type', type)
      if (type === 'module') {
        store.commit('test/increment', 2)
      } else {
        store.commit('increment', 3)
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    # 网络请求

    axios请求库很强大,但只在浏览器中生效。根据项目经验,仍然使用flyio (opens new window)作为请求库,幸运的是,作者已支持ts。网络请求库的封装请参考另一篇uniapp项目中使用flyio封装请求库。

    # 开发代理

    vite支持开发时配置代理。注意,此方式只在浏览器生效,小程序不能使用。

    // vite.config.ts
      server: {
        proxy: {
          '/customer-api': {
            target: 'https://www.baidu.com',
            changeOrigin: true
          }
        }
      },
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    # 添加环境变量import.meta.env

    参考文档 (opens new window),比如在根目录下新建.env.stage,写入

    VITE_APP_TITLE=My App
    
    1

    配置mode

    // package.json
    {
       "stage:h5": "uni --mode stage",
    }
    
    1
    2
    3
    4

    然后在业务代码中使用

    console.log(import.meta.env.VITE_APP_TITLE)
    
    1

    注意要设置ts类型

    // env.d.ts
    interface ImportMetaEnv {
     readonly VITE_APP_TITLE: string
    }
    interface ImportMeta {
     readonly env: ImportMetaEnv
    }
    
    1
    2
    3
    4
    5
    6
    7

    如此,上述代理转发就可以去掉了,在请求配置的baseURL使用import.meta.env.VITE_APP_HOST即可。

    # 生成环境去除console

    vite内置了terser压缩 (opens new window),可以配置

    // vite.config.ts
      build: {
        minify: 'terser',
        terserOptions: {
          compress: {
            //生产环境时移除console
            drop_console: true
          }
        }
      },
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    也可以使用esbuild去除,注意此方式在uniapp开发微信小程序时也会去除console。

      esbuild: { pure: ['console.log'], minify: true },
    
    1

    # 使用vuex-persistedstate

    npm i vuex-persistedstate -D
    
    1
    // src/store/index
    import createPersistedState from 'vuex-persistedstate'
    
    const options = {
      state: {
          count: 1
      },
      plugins: [
        createPersistedState({
          // 本地存储数据的键名 默认vuex
          key: 'uniapp_vue3',
          // 指定需要存储的模块,如果是模块下具体的数据需要加上模块名称,如user.token
          paths: ['count'],
          // 默认存储在localStorage,改为uni storage API
          storage: {
            getItem: key => uni.getStorageSync(key),
            setItem: (key, value) => uni.setStorageSync(key, value),
            removeItem: key => uni.removeStorageSync(key)
          }
        })
      ]
    }
    export const store = createStore(options)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

    # 使用@dcloudio/uni-ui

    参考issue (opens new window)。

    npm i @dcloudio/uni-ui -S
    
    1

    安装之后配置easycom方式,这样项目中可以直接使用了。

    // pages.json
    {
      "easycom": { "autoscan": true, "custom": { "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue" } }
    }
    
    1
    2
    3
    4
    <uni-badge text="1"></uni-badge>
    
    1

    # uniapp-router-patch

    在 uni-app 生成的小程序中 vue-router的兼容写法。

    遇到问题1:

    TS7016: Could not find a declaration file for module 'uniapp-router-patch'.
    
    1

    解决方案1:

    /// src/shims-vue.d.ts
    // 声明全局模块
    declare module 'uniapp-router-patch'
    
    1
    2
    3

    遇到问题2:

    小程序编译报错

    TypeError: Cannot read property 'hasOwnProperty' of undefined
    
    1

    源头为

            if (Vue.prototype.hasOwnProperty('$router')) {
                return;
            }
    
    1
    2
    3

    发现是uniapp-router-patch不支持vue3的use方式引起的,因此改下该npm包对于vue3的支持。

    #uniapp#工程模板
    上次更新: 2022/01/14, 11:17:10
    husky+lint-staged+commitlint规范代码提交
    webpack3老项目升级为vite实践

    ← husky+lint-staged+commitlint规范代码提交 webpack3老项目升级为vite实践→

    最近更新
    01
    提交代码时修改commit消息
    04-09
    02
    如何快速定位bug
    02-20
    03
    云端web项目开发踩坑
    08-25
    更多文章>
    Theme by Vdoing | Copyright © 2021-2025 dwfrost | 粤ICP备2021118995号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式
    ×