当前位置: 首页 > 知识库问答 >
问题:

vue.js - 如何在多个 vue 文件中复用函数,一次定义,处处使用?

姜淇
2024-04-23

如何在多个 vue 文件中复用函数,一次定义,处处使用?

我有一些函数,会在多个 .vue 文件中都使用,我现在是会在每个 .vue 里面都重复定义

比如下面的 openAlgorithmImageHistory 的函数,我在 10 个 vue 文件里面都重复写了,这种体验非常的糟糕

const openAlgorithmImageHistory = (clip_url_hash, keyword_task_id) => {    const url = router.resolve({        name: "AlgorithmImageHistory",        query: { keyword_task_id, clip_url_hash },    }).href;    window.open(url, "_blank");};

按照 chatgpt 给的答案,是把这些函数写到 .js 文件里面,然后在需要使用的 .vue 里面从 .js 导入对应的函数

要在多个Vue文件中复用函数,并确保一次定义,处处使用,你可以使用Vue.js提供的混入(Mixin)功能。

混入允许你定义一组可重用的功能,然后将它们混入到多个Vue组件中。

下面是一个示例:

// mixins.jsexport const myMixin = {  methods: {    someUtilityFunction() {      // 这里是你的实用函数的代码    },    anotherUtilityFunction() {      // 这里是另一个实用函数的代码    }  }}

然后在你的Vue组件中使用混入:

// MyComponent.vue<template>  <div>    <!-- 组件内容 -->  </div></template><script>import { myMixin } from './mixins.js';export default {  // 使用混入  mixins: [myMixin],  // 组件选项  created() {    // 可以直接调用混入的函数    this.someUtilityFunction();    this.anotherUtilityFunction();  }}</script><style>/* 样式 */</style>

这样,你可以在任何需要的Vue组件中使用混入中定义的函数,而无需在每个组件中单独导入或定义它们。

但是这样有问题,就是这个 openAlgorithmImageHistory 函数使用了 router,router 是从 vue 导入的

import { defineComponent, ref, watch } from "vue";import { message, Table } from "ant-design-vue";import axios from "axios";import { useRoute } from "vue-router";import router from "@/router";import { reactive } from "vue";import { onMounted } from "vue";

我怎么在 js 里面 import router from "@/router"; 呢?

我的完整 vue 示例如下:

<template>    <top-bar></top-bar>    <div class="container">        <a-page-header style="border: 1px solid rgb(235, 237, 240)" title="发现的视频" sub-title="通过搜索引擎搜索到的视频"            @back="navigateToRoot" />    </div>    <div class="container">        <div class="container-item">            <a-form :model="formState" :label-col="labelCol" :wrapper-col="wrapperCol">                <a-form-item label="keyword_task_id">                    <a-input v-model:value="formState.keyword_task_id" placeholder="根据 keyword_task_id 筛选,可以不填" />                </a-form-item>                <a-form-item label="parser">                    <a-input v-model:value="formState.parser" placeholder="根据 parser name 筛选,可以不填" />                </a-form-item>                <a-form-item label="clip_url_hash">                    <a-input v-model:value="formState.clip_url_hash" placeholder="根据 clip_url_hash 筛选,可以不填" />                </a-form-item>                <a-form-item label="track_source_id">                    <a-input v-model:value="formState.track_source_id" placeholder="根据 track_source_id 筛选,可以不填" />                </a-form-item>                <a-form-item label="src_meta_uuid">                    <a-input v-model:value="formState.src_meta_uuid" placeholder="根据 src_meta_uuid 筛选,可以不填" />                </a-form-item>                <a-form-item label="company_id">                    <a-form-item name="input-number" no-style>                        <a-input-number v-model:value="formState['company_id']" placeholder="根据 company_id 筛选,可以不填" />                    </a-form-item>                    <span class="ant-form-text">根据 company_id 筛选,可以不填</span>                </a-form-item>                <a-form-item label="匹配数">                    <a-form-item name="input-number" no-style>                        <a-input-number v-model:value="formState['match_count_gte']"                            placeholder="请输入 match_count_gte" />                    </a-form-item>                    <span class="ant-form-text">                        匹配个数的下限,只显示 match_count 大于等于该值的 大于等于的推送记录                    </span>                </a-form-item>                <a-form-item label="limit">                    <a-form-item name="input-number" no-style>                        <a-input-number v-model:value="formState['limit']" placeholder="根据 company_id 筛选,可以不填" />                    </a-form-item>                    <span class="ant-form-text">(用于控制最多显示 n 行)</span>                </a-form-item>                <a-form-item :wrapper-col="{ span: 14, offset: 4 }">                    <a-button type="primary" :loading="searchLoading" @click="sendRequest">提交</a-button>                    <!-- <a-button style="margin-left: 10px">Cancel</a-button> -->                </a-form-item>            </a-form>        </div>    </div>    <div class="container">        <a-table :columns="columns" :dataSource="dataSource" :pagination="false" style="overflow-x: auto">            <template #expandedRowRender="{ record }">                <a-table :columns="subColumns" :data-source="record.found_video_text_matched_details"                    :pagination="false" style="overflow-x: auto">                    <template v-slot:bodyCell="{ text, record, column }">                        <template v-if="column.key === 'meta_uuid'">                            <a href="javascript:;" @click="openMetaDetail(record.meta_uuid)">{{ text }}                            </a>                        </template>                        <template v-if="column.key === 'has_error'">                            <div>                                <template v-if="text"> 异常 </template>                                <template v-else> 无异常 </template>                            </div>                        </template>                    </template>                </a-table>            </template>            <template v-slot:bodyCell="{ text, record, column }">                <template v-if="column.key === 'meta_uuid'">                    <a href="javascript:;" @click="openMetaDetail(record.meta_uuid)">{{ text }}                    </a>                </template>                <template v-if="column.key === 'is_parsed'">                    <div>                        <template v-if="text === 1">✅</template>                        <template v-else-if="text === 0">❌</template>                        <template v-else-if="text === null">处理中</template>                        <template v-else>异常</template>                    </div>                </template>                <template v-if="column.key === 'clip_url'">                    <div>                        <a :href="text" target="_blank">{{ record.clip_url }}</a>                    </div>                </template>                <template v-if="column.key === 'author'">                    <div>                        <a :href="record.author_url" target="_blank">{{ record.author }}</a>                    </div>                </template>                <template                    v-if="column.key === 'dna_vnda_match' || column.key === 'text_clip_titile_match' || column.key === 'face_match'">                    <div>                        <template v-if="text === true">✅</template>                        <template v-else-if="text === false">❌</template>                        <template v-else-if="text === null">(还)未处理</template>                        <template v-else>异常</template>                    </div>                </template>                <template v-if="column.key === 'text_cover_image_ocr_content_match'">                    <div>                        <template v-if="text === true">                            <a-tooltip>                                <template #title>                                    <json-viewer :value="record.found_video_cover_image_ocr" :copyable="copyableOptions"                                        preview-mode>                                    </json-viewer>                                </template>                                ✅                            </a-tooltip>                        </template>                        <template v-else-if="text === false">❌</template>                        <template v-else-if="text === null">(还)未处理</template>                        <template v-else>异常</template>                    </div>                </template>                <template v-if="column.key === 'text_image_search_clip_title_match'">                    <div>                        <template v-if="text === true">                            <a-tooltip>                                <template #title>                                    <json-viewer :value="record.found_video_cover_image_search"                                        :copyable="copyableOptions" preview-mode>                                    </json-viewer>                                </template>                                ✅                            </a-tooltip>                        </template>                        <template v-else-if="text === false">❌</template>                        <template v-else-if="text === null">(还)未处理</template>                        <template v-else>异常</template>                    </div>                </template>            </template>        </a-table>    </div>    <div class="container-footer">        <div>到底了</div>    </div></template><script setup>import { defineComponent, ref, watch } from "vue";import { message, Table } from "ant-design-vue";import axios from "axios";import { useRoute } from "vue-router";import router from "@/router";import { reactive } from "vue";import { onMounted } from "vue";onMounted(async () => {    document.title = "解析器——解析历史"; // 设置浏览器标签页的标题});const labelCol = { span: 4 };const wrapperCol = { span: 14 };const route = useRoute();const formState = reactive({    keyword_task_id: route.query.keyword_task_id || null,    track_source_id: route.query.track_source_id || null,    clip_url_hash: route.query.clip_url_hash || null,    src_meta_uuid: route.query.src_meta_uuid || null,    company_id: route.query.company_id || null,    limit: route.query.limit || 200,    match_count_gte: route.query.match_count_gte || null,    parser: route.query.parser || null,});const searchLoading = ref(false);const dataSource = ref([]);const subColumns = [    {        title: "被匹配的母本",        dataIndex: "meta_uuid",        key: "meta_uuid",        sorter: (a, b) => a.name.localeCompare(b.name),    },    {        title: "video_id",        dataIndex: "video_id",        key: "video_id",        sorter: (a, b) => a.messages - b.messages,    },    {        title: "score",        dataIndex: "score",        key: "score",        sorter: (a, b) => a.messages - b.messages,    },    {        title: "算法来源",        dataIndex: "source_type",        key: "source_type",        sorter: (a, b) => a.name.localeCompare(b.name),    },    {        title: "匹配详情",        dataIndex: "highlight",        key: "highlight"    },];const columns = [    {        title: "video_id",        dataIndex: "id",        key: "id",        sorter: (a, b) => a.messages - b.messages,    },    {        title: "domain",        dataIndex: "domain",        key: "domain",        sorter: (a, b) => a.name.localeCompare(b.name),    },    {        title: "clip_url_hash",        dataIndex: "clip_url_hash",        key: "clip_url_hash",        sorter: (a, b) => a.name.localeCompare(b.name),    },    {        title: "clip_url",        dataIndex: "clip_url",        key: "clip_url",        sorter: (a, b) => a.name.localeCompare(b.name),    },    {        title: "clip_title",        dataIndex: "clip_title",        key: "clip_title",        sorter: (a, b) => a.name.localeCompare(b.name),    },    {        title: "publish_date(平台时间)",        dataIndex: "publish_date",        key: "publish_date",        sorter: (a, b) => a.messages - b.messages,    },    {        title: "author",        dataIndex: "author",        key: "author",        sorter: (a, b) => a.name.localeCompare(b.name),    },    {        title: "时长(秒)",        dataIndex: "duration",        key: "duration",        sorter: (a, b) => a.messages - b.messages,    },    {        title: "view_count",        dataIndex: "view_count",        key: "view_count",        sorter: (a, b) => a.messages - b.messages,    },    {        title: "like_count",        dataIndex: "like_count",        key: "like_count",        sorter: (a, b) => a.messages - b.messages,    },    {        title: "forward_count",        dataIndex: "forward_count",        key: "forward_count",        sorter: (a, b) => a.messages - b.messages,    },    {        title: "comments_count",        dataIndex: "comments_count",        key: "comments_count",        sorter: (a, b) => a.messages - b.messages,    },    {        title: "video_download_url",        dataIndex: "video_download_url",        key: "video_download_url",        sorter: (a, b) => a.messages - b.messages,    },    {        title: "视频发现时间",        dataIndex: "created_at",        key: "created_at",        sorter: (a, b) => a.messages - b.messages,    },    {        title: "updated_at(utc)",        dataIndex: "updated_at",        key: "updated_at",        sorter: (a, b) => a.messages - b.messages,    },    {        title: "dna 匹配情况",        dataIndex: "dna_vnda_match",        key: "dna_vnda_match",        sorter: (a, b) => a.messages - b.messages,    },    {        title: "视频标题文本匹配情况",        dataIndex: "text_clip_titile_match",        key: "text_clip_titile_match",        sorter: (a, b) => a.messages - b.messages,    },    {        title: "视频封面图 ocr 文本匹配情况",        dataIndex: "text_cover_image_ocr_content_match",        key: "text_cover_image_ocr_content_match",        sorter: (a, b) => a.messages - b.messages,    },    {        title: "视频封面图 + 以图搜图+ title 文本匹配情况",        dataIndex: "text_image_search_clip_title_match",        key: "text_image_search_clip_title_match",        sorter: (a, b) => a.messages - b.messages,    },    {        title: "视频封面图人脸匹配情况",        dataIndex: "face_match",        key: "face_match",        sorter: (a, b) => a.messages - b.messages,    },];const openSearchEngineCustom = (track_source_id) => {    let url = router.resolve({        name: "SearchEngineCustom",    }).href;    url += `?track_source_id=${track_source_id}`;    window.open(url, "_blank");};const sendRequest = () => {    // if (!keywordTaskId.value) {    //   message.warning("Please input a valid task ID.");    //   return;    // }    searchLoading.value = true; // 开始请求数据,设置 loading 状态为 true    const urlParams = new URLSearchParams();    for (const key in formState) {        if (Reflect.has(formState, key)) {            if (formState[key] === "") {                formState[key] = null;            }            if (formState[key] !== null) {                urlParams.set(key, formState[key]);            }        }    }    window.history.replaceState(null, null, `?${urlParams.toString()}`);    const queryParams = {};    for (const key in formState) {        if (Reflect.has(formState, key)) {            if (formState[key] !== null) {                queryParams[key] = formState[key];            }        }    }    axios        .get(            `/taisan_console_api/v1/video/list_found_videos`,            {                params: queryParams,            }        )        .then((response) => {            const updatedData = response.data.map((item) => {                return { ...item, key: item.id };            });            dataSource.value = updatedData;        })        .catch((err) => {            message.error("Failed to fetch data.");            console.error(err);        })        .finally(() => {            searchLoading.value = false;        });};for (const key in formState) {    if (Reflect.has(formState, key)) {        if (formState[key] !== null && key !== "limit") {            sendRequest();            break;        }    }}const openParserHistoryDetail = (clip_url_hash, keyword_task_id) => {    const url = router.resolve({        name: "ParserHistoryDetail",        query: { clip_url_hash, keyword_task_id },    }).href;    window.open(url, "_blank");};const openAlgorithmTextHistory = (clip_url_hash, keyword_task_id) => {    const url = router.resolve({        name: "AlgorithmTextHistory",        query: { keyword_task_id, clip_url_hash },    }).href;    window.open(url, "_blank");};const openAlgorithmImageHistory = (clip_url_hash, keyword_task_id) => {    const url = router.resolve({        name: "AlgorithmImageHistory",        query: { keyword_task_id, clip_url_hash },    }).href;    window.open(url, "_blank");};const openMetaDetail = (meta_uuid) => {    let url = router.resolve({        name: "MetaDetail",        query: { meta_uuid },    }).href;    window.open(url, "_blank");};const openKeywordPushed = (keyword_task_id) => {    let url = router.resolve({        name: "KeywordPushed",    }).href;    url += `?keyword_task_id=${keyword_task_id}`;    window.open(url, "_blank");};const navigateToRoot = () => {    router.push("/");};</script><style>.gray-placeholder {    color: gray;}.container {    margin: 0 auto;    /* 居中显示 */    margin-top: 20px;    max-width: 1440px;    /* 设置最大宽度为900px */    background-color: #ffffff;    /* 浅灰色 */    border-radius: 0.25rem;}.container-item {    padding: 25px;    border-width: 0 0 1px;    margin-bottom: 20px;}.theme-icon {    width: 64px;    /* 设置图标的宽度 */    height: 64px;    /* 设置图标的高度 */}.container-footer {    display: flex;    justify-content: center;    margin-top: 20px;    /* 调整顶部间距 */}</style>

共有3个答案

锺离慈
2024-04-23

vue中的组合式函数可以帮助你解决。vue3中的组合式函数类似react概念中的Hooks

// useOpenAlgorithmImageHistory.tsimport {useRouter} from 'vue-router'import {ref} from 'vue'const useOpenAlgorithmImageHistory = (payload1, payload2) => {    const router = useRouter()    const url = ref<string>('')    const open = (payload3, payload4) => {        url.value = router.resolve({            name: "AlgorithmImageHistory",            query: { payload1, payload2, payload3, payload4 },        }).href        window.open(url.value, '_blank')    }    return {open}}

在组合式函数内 可以引入vue一切合法的模块
注:
可能ref在提问者提出的需求里用不到,这只是演示 响应式在组合式函数里依然可以运行

在你的页面:

// index.vue<script setup>import useOpenAlgorithmImageHistory from './useOpenAlgorithmImageHistory.ts'const {open} = useOpenAlgorithmImageHistory('参数A', '参数B')const onClick = () => {    open('参数C', '参数D')}</script>
梁明辉
2024-04-23

可以用函数式组件

冯飞鹏
2024-04-23

单独的js文件里面也可以 import { router } from '/router.js 的。
不光是 vue-routervuex 以及UI库的一些消息弹窗也可以这样用。

举个例子 �� vue-element-admin/src/permission.js at master

import router from './router'import store from './store'import { Message } from 'element-ui'import NProgress from 'nprogress' // progress barimport 'nprogress/nprogress.css' // progress bar styleimport { getToken } from '@/utils/auth' // get token from cookieimport getPageTitle from '@/utils/get-page-title'NProgress.configure({ showSpinner: false }) // NProgress Configurationconst whiteList = ['/login', '/auth-redirect'] // no redirect whitelistrouter.beforeEach(async(to, from, next) => {  // start progress bar  NProgress.start()  // set page title  document.title = getPageTitle(to.meta.title)  // determine whether the user has logged in  const hasToken = getToken()  if (hasToken) {    if (to.path === '/login') {      // if is logged in, redirect to the home page      next({ path: '/' })      NProgress.done() // hack: https://github.com/PanJiaChen/vue-element-admin/pull/2939    } else {      // determine whether the user has obtained his permission roles through getInfo      const hasRoles = store.getters.roles && store.getters.roles.length > 0      if (hasRoles) {        next()      } else {        try {          // get user info          // note: roles must be a object array! such as: ['admin'] or ,['developer','editor']          const { roles } = await store.dispatch('user/getInfo')          // generate accessible routes map based on roles          const accessRoutes = await store.dispatch('permission/generateRoutes', roles)          // dynamically add accessible routes          router.addRoutes(accessRoutes)          // hack method to ensure that addRoutes is complete          // set the replace: true, so the navigation will not leave a history record          next({ ...to, replace: true })        } catch (error) {          // remove token and go to login page to re-login          await store.dispatch('user/resetToken')          Message.error(error || 'Has Error')          next(`/login?redirect=${to.path}`)          NProgress.done()        }      }    }  } else {    /* has no token*/    if (whiteList.indexOf(to.path) !== -1) {      // in the free login whitelist, go directly      next()    } else {      // other pages that do not have permission to access are redirected to the login page.      next(`/login?redirect=${to.path}`)      NProgress.done()    }  }})router.afterEach(() => {  // finish progress bar  NProgress.done()})
 类似资料:
  • 主要内容:GCC编译多文件项目通过前面几节的学习,读者已经了解了如何使用 gcc(g++)指令调用 GCC 编译器编译(包括预处理、编译、汇编和链接)C 或者 C++ 源代码,例如: [root@bogon demo]# ls demo1.c  demo2.c [root@bogon demo]# cat demo1.c #include<stdio.h> int main(){     printf("GCC:https:/

  • 我对Flyway完全陌生,但我正在尝试使用https://github.com/flyway/flyway-docker描述的docker-compose flyway mysql安排来迁移许多相同的测试数据库 据我所知,< code>migrate命令可以在它的< code>-schemas参数中接受多个模式,但是它似乎只将实际的SQL迁移应用于列表中的第一个模式。 例如,当我使用< code>

  • Parsing error: The keyword 'interface' is reserved 只有在 .vue 文件中定义 interface 才报错 eslint.config.js:

  • 问题内容: 我想使用PHP上传文件,但问题是我不知道要上传多少文件。 我的问题是,如果使用该如何上传文件? 我将仅添加“文件”框,并使用JavaScript创建更多要上传的文件输入,但是如何在PHP中处理它们呢? 问题答案: 请参阅:$ _FILES ,处理文件上传

  • 我正在尝试在批处理文件中定义和使用变量。看起来应该很简单: 我得到的输出如下: 这是怎么回事?为什么我的变量没有被回声?