如何在多个 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>
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>
可以用函数式组件
单独的js文件里面也可以 import { router } from '/router.js
的。
不光是 vue-router
,vuex
以及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 ,处理文件上传
我正在尝试在批处理文件中定义和使用变量。看起来应该很简单: 我得到的输出如下: 这是怎么回事?为什么我的变量没有被回声?