• vue3的那些事


    没有特别的幸运,那么就特别的努力!!!

    vue3 + vite + ts + vant + axios + sass

    vite官网:

    搭建第一个 Vite 项目 (vite + vue + ts)

    兼容性注意
    Vite 需要 Node.js 版本 14.18+,16+。然而,有些模板需要依赖更高的 Node 版本才能正常运行,当你的包管理器发出警告时,请注意升级你的 Node 版本。

    nvm管理node多版本。1

    // 搭建第一个 Vite 项目 (vite + vue + ts)
    
    // npm (本篇采用npm搭建)
    npm init vite@latest 
    
    // yarn
    yarn create vite
    
    // pnpm
    pnpm create vite
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    # npm 6.x
    npm create vite@latest vite-vue3 --template vue
    
    # npm 7+, extra double-dash is needed:
    npm create vite@latest vite-vue3 -- --template vue
    
    # yarn
    yarn create vite vite-vue3 --template vue
    
    # pnpm
    pnpm create vite vite-vue3 --template vue
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    项目启动

    
    cd vite-vue3
    
    npm install
    npm run dev
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    代码规范 (格式化、提示)

    eslint

    # 自动生成配置文件并安装下面四个依赖
    npx eslint --init
    
    # 或者手动创建文件
    # npm i eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-vue -D
    
    • 1
    • 2
    • 3
    • 4
    • 5

    prettier

    npm i prettier eslint-config-prettier eslint-plugin-prettier -D
    
    • 1

    创建prettier文件

    // prettier.cjs
    
    module.exports = {
      printWidth: 100,
      tabWidth: 2,
      useTabs: false, // 是否使用tab进行缩进,默认为false
      singleQuote: true, // 是否使用单引号代替双引号,默认为false
      semi: true, // 行尾是否使用分号,默认为true
      arrowParens: 'always',
      endOfLine: 'auto',
      vueIndentScriptAndStyle: true,
      htmlWhitespaceSensitivity: 'strict',
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    配置eslintrc

    // eslintrc.cjs
    
    module.exports = {
      root: true, // 停止向上查找父级目录中的配置文件
      env: {
        browser: true,
        es2021: true,
        node: true,
      },
      extends: [
        'eslint:recommended',
        'plugin:vue/vue3-essential',
        'plugin:@typescript-eslint/recommended',
        'plugin:prettier/recommended',
        'prettier', // eslint-config-prettier 的缩写
      ],
      parser: 'vue-eslint-parser', // 指定要使用的解析器
      // 给解析器传入一些其他的配置参数
      parserOptions: {
        ecmaVersion: 'latest', // 支持的es版本
        parser: '@typescript-eslint/parser',
        sourceType: 'module', // 模块类型,默认为script,我们设置为module
      },
      plugins: ['vue', '@typescript-eslint', 'prettier'], // eslint-plugin- 可以省略
      rules: {
        'vue/multi-word-component-names': 'off',
        '@typescript-eslint/no-var-requires': '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

    配置 tsconfig

    // tsconfig.json
    
    {
      "compilerOptions": {
        "target": "ESNext",
        "useDefineForClassFields": true,
        "module": "ESNext",
        "moduleResolution": "Node",
        "strict": true,
        "jsx": "preserve",
        "sourceMap": true,
        "resolveJsonModule": true,
        "isolatedModules": true,
        "esModuleInterop": true,
        "lib": ["ESNext", "DOM"],
        "skipLibCheck": true,
        // 👆是初始化默认配置
        /*
          在ts中导入js模块会报错找不到类型声明
          解决方法一:
            仅设置 "allowJs": true 即可
            注:allowJs设置true时,下方include不可以加入'src/**\/*.js',否则报错'无法写入文件xx因为它会覆盖输入文件'
          方法二:
            仅在 env.d.ts 中加入 declare module '*.js'; 模块定义即可
    
          总结:和 "include": ["src/**\/*.js"] 没有任何关系
        */
        "allowJs": true, // 允许编译器编译JS,JSX文件
        "baseUrl": "./",
        // "typeRoots": [
        //   "node_modules/@types" // 默认会从'node_modules/@types'路径去引入声明文件
        // ],
        // "types": ["node"] // 仅引入'node'模块
        // "paths"是相对于"baseUrl"进行解析
        // 在vite.config里配置了路径别名resolve.alias,为了让编译 ts 时也能够解析对应的路径,我们还需要配置 paths 选项
        "paths": {
          "@/*": ["src/*"],
        }
      },
      "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
      // references属性是 TypeScript 3.0 的新特性,允许将 TypeScript 程序拆分结构化(即拆成多个文件,分别配置不同的部分)。
      "references": [{ "path": "./tsconfig.node.json" }]
    }
    
    • 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

    tsconfig.node.json

    {
      "compilerOptions": {
        "composite": true,
        "module": "ESNext",
        "moduleResolution": "Node",
        "allowSyntheticDefaultImports": true
      },
      "include": ["vite.config.ts", "config/index.ts"]
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    CSS 预处理器

    less安装使用

    // npm 安装
    npm install less
    npm install less-loader
    
    // yarn 安装
    yarn add less less-loader
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    // 使用
    < style lang="less" scoped></ style>
    
    • 1
    • 2

    sass安装使用

    // npm 安装
    npm install -D sass sass-loader
    
    // yarn 安装
    yarn add sass sass-loader
    
    • 1
    • 2
    • 3
    • 4
    • 5
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    vant 安装

    vant3官网地址:
    安装

    // npm 安装
    npm i vant
    
    // yarn 安装
    yarn add vant
    
    // 通过 pnpm 安装
    pnpm add vant
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    常规用法

    import { createApp } from 'vue'
    import './style.css'
    // 1. 引入你需要的组件
    import { Button } from 'vant';
    // 2. 引入组件样式
    import 'vant/lib/index.css';
    import App from './App.vue'
    
    const app = createApp(App)
    
    // 3. 注册你需要的组件
    app.use(Button);
    
    app.mount('#app');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    按需引入组件样式

    // 通过 npm 安装
    npm i unplugin-vue-components -D
    
    // 通过 yarn 安装
    yarn add unplugin-vue-components -D
    
    // 通过 pnpm 安装
    pnpm add unplugin-vue-components -D
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    配置插件
    vite 的项目,在 vite.config.js 文件中配置插件:

    import vue from '@vitejs/plugin-vue';
    import Components from 'unplugin-vue-components/vite';
    import { VantResolver } from 'unplugin-vue-components/resolvers';
    
    export default {
      plugins: [
        vue(),
        Components({
          resolvers: [VantResolver()],
        }),
      ],
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    使用组件

    <template>
      <van-button type="primary" />
    template>
    
    • 1
    • 2
    • 3

    Rem 布局适配

    // npm 安装
    npm install -D postcss-pxtorem lib-flexible
    
    // yarn 安装
    yarn add postcss-pxtorem lib-flexible
    
    • 1
    • 2
    • 3
    • 4
    • 5

    根目录下面新建一个 postcss.config.js 文件

    // postcss.config.js
    module.exports = {
      plugins: {
        'postcss-pxtorem': {
          rootValue: 37.5,
          propList: ['*'],
        },
      },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    底部适配 - 对于ios系统

    
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover"
    />
    
    
    <van-nav-bar safe-area-inset-top />
    
    
    <van-number-keyboard safe-area-inset-bottom />
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    vue-router

    1.安装

    npm i vue-router@4
    
    • 1

    2.创建路由

    // src/router/index.ts
    
    //现在创建router的方式与vue2.x的版本已经很不同了
    import { createRouter, createWebHashHistory } from "vue-router";
    import { routes } from "./routes";
    
    const router = createRouter({
      history: createWebHashHistory(), //替代之前的mode,是必须的
      routes,
    });
    
    router.beforeEach((to, from, next) => {
      document.title = to.meta.title as string || '浙里普法'
      next()
    })
    export default router;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    // src/router/routes.ts
    
    import { RouteRecordRaw } from "vue-router";
    export const routes: Array<RouteRecordRaw> = [
      {
        path: "/",
        redirect: "/index",
      },
      {
        path: "/index",
        name: "Index",
        component: () => import("../view/index.vue"),
        meta: {
          nav: true,
          title: '首页'
        }
      },
    
    ];
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.挂载路由

    // src/main.ts
    import { createApp } from 'vue';
    import App from './App.vue';
    import router from './router/index'; //引入vue-router
    
    const app = createApp(App);
    
    app.use(router); // 挂载到app上
    app.mount('#app');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4.使用

    <template>
      <router-view />
    </template>
    
    • 1
    • 2
    • 3

    Axios

    1.安装

    // npm 安装
    npm i axios
    
    // yarn 安装
    yarn add axios
    
    • 1
    • 2
    • 3
    • 4
    • 5
    // src/utils/http/axios.ts
    
    import axios, { AxiosResponse, AxiosRequestConfig, AxiosError } from 'axios';
    import type { Response } from './types';
    // import { auth } from '@/utils';
    import { Toast } from 'vant';
    import router from '../../router';
    
    axios.defaults.baseURL = '/api';
    axios.defaults.timeout = 1000 * 60;
    axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
    
    // 创建axios实例
    const service = axios.create({
      // 根据不同env设置不同的baseURL
      baseURL: import.meta.env.VITE_APP_API_BASE_URL,
    });
    
    // axios实例拦截请求
    service.interceptors.request.use(
      (config: AxiosRequestConfig) => {
        config.headers = {
          ...config.headers,
          // ...auth.headers(), // 你的自定义headers,如token等
        };
        return config;
      },
      (error: AxiosError) => {
        return Promise.reject(error);
      }
    );
    
    // axios实例拦截响应
    service.interceptors.response.use(
      // 2xx时触发
      (response: AxiosResponse<Response>) => {
        // response.data就是后端返回的数据,结构根据你们的约定来定义
        const { code, message } = response.data;
        let errMessage = '';
        switch (code) {
          case 0:
            break;
          case 1: // token过期
            errMessage = 'Token expired';
            router.push('/login');
            break;
          case 2: // 无权限
            errMessage = 'No permission';
            break;
          // default:
          //   errMessage = message;
          //   break;
        }
        if (errMessage) Toast.fail(errMessage);
        return response;
      },
      // 非2xx时触发
      (error: AxiosError) => {
        Toast.fail('Network Error...');
        return Promise.reject(error);
      }
    );
    
    export type { AxiosResponse, AxiosRequestConfig };
    
    export default service;
    
    • 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
    // src/utils/http/index.ts
    
    import service, { AxiosRequestConfig } from './axios';
    export * from './types';
    
    export const request = <T = any>(config: AxiosRequestConfig): Promise<T> => {
      return new Promise((resolve, reject) => {
        service
          .request(config)
          .then((res) => {
            // 一些业务处理
            resolve(res.data);
          })
          .catch((err) => {
            console.log('request fail:', err);
          });
      });
    };
    
    const http = {
      get<T = any>(url: string, params = {}, config?: AxiosRequestConfig): Promise<T> {
        return request({ url, params, ...config, method: 'GET' });
      },
      post<T = any>(url: string, data = {}, config?: AxiosRequestConfig): Promise<T> {
        return request({ url, data, ...config, method: 'POST' });
      },
      put<T = any>(url: string, data = {}, config?: AxiosRequestConfig): Promise<T> {
        return request({ url, data, ...config, method: 'PUT' });
      },
      delete<T = any>(url: string, data = {}, config?: AxiosRequestConfig): Promise<T> {
        return request({ url, data, ...config, method: 'DELETE' });
      },
      // 上传文件,指定 'Content-Type': 'multipart/form-data'
      upload<T = any>(url: string, data = {}, config?: AxiosRequestConfig): Promise<T> {
        return request({
          url,
          data,
          ...config,
          method: 'POST',
          headers: { 'Content-Type': 'multipart/form-data' },
        });
      },
    };
    
    export default http;
    
    • 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
    // src/utils/http/types.ts
    
    // 和后端约定好接口返回的数据结构
    export interface Response<T = any> {
      data: string[];
      code: number | string;
      message: string;
      result: T;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    示例页面

    banner列表页面

    <script setup>
    import ResourceList from '@/components/ResourceList.vue'
    import { monthlyResourceList } from '@/service/api/common'
    import { onMounted, ref } from 'vue'
    import { useRoute, useRouter } from "vue-router";
    
    const $route = useRoute()
    const $router = useRouter()
    const list = ref([])
    const loading = ref(false);
    const finished = ref(false);
    const refreshing = ref(false);
    const params = ref({
      relationId: $route.query.id,
      relationType: 'banner',
      currentPage: 1,
      pageSize: 10
    })
    onMounted(() => {
      document.title = $route.query.name
      getColumnResourceList()
    })
    const getColumnResourceList = () => monthlyResourceList(params.value).then(res => {
      loading.value = true
      if (res.success) {
        loading.value = false
        list.value = [...list.value,...res.data]
    
        // 如果列表数据条数>=总条数,不再触发滚动加载
        if (list.value.length >= res.totalCount) {
          finished.value = true
        }
      }
    })
    const onRefresh = () => {
      params.value.currentPage = 1
      finished.value = false;
      refreshing.value = false
      list.value = []
      getColumnResourceList();
    };
    const onLoad1 = () => {
      params.value.currentPage++
      getColumnResourceList()
    }
    const toInfo = row => {
      const { type, resourceSource, resourceId, id: relationId, relationType = 'banner' } = row
      $router.push({
        path: '/detail',
        query: { type, resourceSource, resourceId, relationId, relationType }
      })
    }
    
    </script>
    
    <template>
      <div class='monthInfo'>
        <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
        <van-list
          v-model:loading="loading"
          :finished="finished"
          finished-text="没有更多了"
          :immediate-check="false"
          @load="onLoad1"
        >
          <div v-for="(item, i) in list" :key="i">
            <ResourceList :info="item" @click="toInfo(item)"></ResourceList>
          </div>
        </van-list>
        </van-pull-refresh>
        
      </div>
    </template>
    
    <style lang='scss' scoped>
    .monthInfo {
      padding: 22px 16px;
    }
    </style>
    
    • 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

    项目地址

    为了安全协议:项目地址api 已全部替换(望理解!!!)
    https://gitee.com/hammer1010_admin/vue3-vite




    vue3 开发

    父组件传参

    defineProps

    父组件

    <template>
      <Children :msg="msg" :list="list"></Children>
    </template>
    
    <script setup lang="ts">
    import { ref, reactive } from 'vue'
    import Children from './Children.vue'
    
    const msg = ref('hello 啊,树哥')
    const list = reactive<number[]>([1, 2, 3])
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    子组件

    <template>
      <div>
        <p>msg:{{msg}}</p>
        <p>list:{{list}}</p>
      </div>
    </template>
    
    <script setup lang="ts">
    import { defineProps } from "vue";
    
    const { msg, list } = defineProps(['msg', 'list'])
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    withDefaults 定义默认值

    <script setup lang="ts">
    import { defineProps } from "vue";
    withDefaults(
      defineProps<{ msg?: (string | number | boolean), title?: string }>(),{
          msg:'hello vite',
          title:'默认标题'
      }
    );
    
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    defineEmits

    子组件传递

    <template>
      <div>
        <p>msg:{{msg}}</p>
        <p>list:{{list}}</p>
        <button @click="onChangeMsg">改变msg</button>
      </div>
    </template>
    
    <script setup lang="ts">
    type Props = {
      msg?: string,
      list?: number[]
    }
    
    withDefaults(defineProps<Props>(), {
      msg: '张麻子',
      list: () => [4, 5, 6]
    })
    
    const emits = defineEmits(['changeMsg'])
    const onChangeMsg = () => {
    emits('changeMsg','黄四郎')
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    父组件接收

    <template>
      <Children :msg="msg" :list="list" @changeMsg="changeMsg"></Children>
    </template>
    
    <script setup lang="ts">
    import { ref, reactive } from 'vue'
    import Children from './Children.vue'
    
    const msg = ref('hello 啊,树哥')
    const list = reactive<number[]>([1, 2, 3])
    
    const changeMsg = (v: string) => {
      msg.value = v
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    ref VS reactive

    1. reactive返回一个对象的响应式代理。
    2. ref参数一般接收简单数据类型,若ref接收对象为参数,本质上会转变为reactive方法
    3. 在JS中访问ref的值需要手动添加.value,访问reactive不需要
    4. 响应式的底层原理都是Proxy

    watch

    侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。

    监听ref定义的一个响应式数据

    <script setup lang="ts">
    import { ref, watch } from "vue";
    
    const str = ref('一个值')
    
    //3s后改变str的值
    setTimeout(() => { str.value = '3s后一个值' }, 3000)
    
    watch(str, (newV, oldV) => {
      console.log(newV, oldV) //3s后一个值 一个值
    })
    
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    监听多个ref

    <script setup lang="ts">
    import { ref, watch } from "vue";
    
    let name = ref('树哥')
    let age = ref(18)
    
    //3s后改变值
    setTimeout(() => {
      name.value = '我叫树哥'
      age.value = 19
    }, 3000)
    
    watch([name, age], (newV, oldV) => {
      console.log(newV, oldV) // ['我叫树哥', 19]  ['树哥', 18]
    })
    
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    监听reactive 定义响应式对象的单一属性

    <script setup lang="ts">
    import { reactive, watch } from "vue";
    
    let info = reactive({
      name: '张麻子',
      age: 18,
      obj: {
        str: '彼时彼刻,恰如此时此刻'
      }
    })
    
    //3s后改变s值
    setTimeout(() => {
      info.obj.str = 'to be or not to be'
    }, 3000)
    
    // 需要自己开启 deep:true深度监听,不然不发触发 watch 的回调函数
    watch(() => info.obj, (newV, oldV) => {
      console.log(newV, oldV)
    }, {
      deep: true
    })
    
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    watch VS watchEffect

    watch只有监听的值发生变化的时候才会执行
    watchEffect 立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。
    wacthEffect 无法获取到原值,只能得到变化后的值
    watchEffect 不用指明监视哪个属性,监视的回调中用到哪个属性就监视哪个属性

    生命周期

    在这里插入图片描述

    keep-alive 缓存组件

    作用和vue2一致,只是生命周期名称有所更改

    <template>
      <div class="full-screen">
        <router-view v-slot="{ Component }">
          <keep-alive :include="['Index', 'secondaryPage', 'resource', 'monthInfo', 'collect']">
            <component :is="Component" />
          </keep-alive>
        </router-view>
      </div>
    </template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    provide/inject

    provide 可以在祖先组件中指定我们想要提供给后代组件的数据或方法,而在任何后代组件中,我们都可以使用 inject 来接收 provide 提供的数据或方法。
    父组件

    <template>
        <router-view v-if="isRouterView"></router-view>
    </template>
    
    <script lang="ts" setup>
    import { ref, provide, nextTick } from "vue";
    const isRouterView = ref(true);
    //父组件刷新方法
    const reload = () => {
      isRouterView.value = false;
      nextTick(() => {
        isRouterView.value = true;
      })
    }
    //provide进行注册
    provide('reload', reload);
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    子/孙组件

    <script lang="ts" setup>
    //子孙组件引入inject
    import { ref,inject } from "vue";
    
    
    const reload = inject("reload");
    
    //调用方法使用
    const handleClick = (val: any) => {
       if (typeof reload == "function") reload();
    };
    </script >
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    希望能帮助到大家,同时祝愿大家在开发旅途中愉快!!!

    拿着 不谢 请叫我“锤” !!!


    1. 可以运用nvm管理node多版本,其中最常见就是环境依赖问题 (npm 安装报错 npm ERR! Unexpected token ‘.’) 可以参考这篇文章:https://www.cnblogs.com/yilei-zero/p/16003054.html ↩︎

  • 相关阅读:
    女毒动力SpringBoot学习截图
    栈在递归中的应用
    布谷鸟搜索算法的改进及其在优化问题中的应用(Matlab代码实现)
    关于SparkRdd和SparkSql的几个指标统计,scala语言,打包上传到spark集群,yarn模式运行
    C# 实验一
    行业下滑,海尔智家继续逆增双11全网第一
    centos7.9 扩容swap分区
    Commvault+XSKY 推出基于 Object Lock 的防勒索病毒联合方案
    若依管理框架-漏洞复现
    《Linux高性能服务器编程》--TCP/IP协议族
  • 原文地址:https://blog.csdn.net/hammer1010/article/details/127378452