Vue-Fetch-Async-Page可行性研究(终)

接上一篇Vue-Fetch-Async-Page可行性研究(续4)-CSDN博客
上篇引入了vue-i18n、material icon和roboto字体并适配了组件内国际化,遗留了Vuetify组件国际化及其配置抽取到JS请求返回、路由表抽取到Json请求返回、主题切换功能、App.js的抽取等改进工作。

MenuRoutes.json


// MenuRoutes.json
[

    {"menu":"Go to VTable","path": "/VTable", "name": "VTable", "fetchPath": "./VTablePage.js"},
    {"menu":"Go to VBtn","path": "/VBtn", "name": "VBtn", "fetchPath": "./VBtnPage.js"},
    {"menu":"Go to Echarts","path": "/echarts", "name": "echarts", "fetchPath": "./EchartsPage.js"}
]

en_US.js

// /locale/en_US.js 经过分析后发现Vuetify只对en作了国际化配置,没有中文简体,分析加载的数据结构后将结构复制到en_US.js保存
export default {
    badge: 'Badge',
    open: 'Open',
    close: 'Close',
    dismiss: 'Dismiss',
    confirmEdit: {
        ok: 'OK',
        cancel: 'Cancel',
    },
    dataIterator: {
        noResultsText: 'No matching records found',
        loadingText: 'Loading items...',
    },
    dataTable: {
        itemsPerPageText: 'Rows per page:',
        ariaLabel: {
            sortDescending: 'Sorted descending.',
            sortAscending: 'Sorted ascending.',
            sortNone: 'Not sorted.',
            activateNone: 'Activate to remove sorting.',
            activateDescending: 'Activate to sort descending.',
            activateAscending: 'Activate to sort ascending.',
        },
        sortBy: 'Sort by',
    },
    dataFooter: {
        itemsPerPageText: 'Items per page:',
        itemsPerPageAll: 'All',
        nextPage: 'Next page',
        prevPage: 'Previous page',
        firstPage: 'First page',
        lastPage: 'Last page',
        pageText: '{0}-{1} of {2}',
    },
        ...
};

zh_CN.js

// /locale/zh_CN.js 参照英文作中文的修改,以验证国际化切换后Vuetify组件是否同步切换
export default {
  badge: "徽章",
  open: "打开",
  close: "关闭",
  dismiss: "关闭",
  confirmEdit: {
      ok: "确定",
      cancel: "取消"
  },
  dataIterator: {
      noResultsText: "没有匹配的记录",
      loadingText: "加载中..."
  },
  dataTable: {
      itemsPerPageText: "每页条数:",
      ariaLabel: {
          sortDescending: "倒序",
          sortAscending: "正序",
          sortNone: "未排序",
          activateNone: "激活以取消排序",
          activateDescending: "激活以倒序",
          activateAscending: "激活以正序"
      },
      sortBy: "排序"
  },
  dataFooter: {
      itemsPerPageText: "每页条数:",
      itemsPerPageAll: "全部",
      nextPage: "下一页",
      prevPage: "上一页",
      firstPage: "第一页",
      lastPage: "最后一页",
      pageText: "{0}-{1} 共 {2}"
  },
  ...
}

VTablePage.js

// VTablePage.js 借用Vuetify v-data-table组件验证Vuetify组件国际化切换,注意看分页部分
export default {
    template:
        `
        <v-data-table :items="items"></v-data-table>
        `,
    setup() {
        const items = [
            {
                name: 'African Elephant',
                species: 'Loxodonta africana',
                diet: 'Herbivore',
                habitat: 'Savanna, Forests',
            },
        ];
        return { items };
    }
};

App.js

// App.js
const { createApp, ref, reactive, computed, watch, watchEffect, h } = Vue;
const { createVuetify } = Vuetify

const CTranslateMenu = { // 国际化切换改用Vuetify下拉面板组件实现更好些
  template:
    `
    <v-menu
      v-model="menu"
      location="bottom end"
    >
      <template v-slot:activator="{ props }">
        <v-btn icon="mdi-translate" v-bind="props"></v-btn>
      </template>

      <v-list>
        <v-list-item
          v-for="(item, index) in items"
          :key="index"
          :value="item.value"
          color="primary"
          :active="locale === item.value"
          @click="onToggleLocale(item.value)"
        >
          <v-list-item-title>{{ item.title }}</v-list-item-title>
        </v-list-item>
      </v-list>
    </v-menu>
  `,
  setup() {
    const menu = ref(false);
    const i18n = VueI18n.useI18n();
    const locale = ref(i18n.locale.value);
    const onToggleLocale = (value) => {
      i18n.locale.value = value;
      locale.value = value;
    }
    const items = [
      { title: 'English', value: 'en_US' },
      { title: '简体中文', value: 'zh_CN' },
    ]
    return { items, menu, locale, onToggleLocale };
  },
};
const initApp = (MenuRoutes, i18nMessages) => {
  const AppConfig = {
    name: 'App',
    components: {
      'c-translate-menu': CTranslateMenu
    },
    template:
      `<v-locale-provider :locale="locale">
        <v-app :theme="theme">
          <v-app-bar color="teal-darken-4" image="assets/jpg/1080.jpg">
            <template v-slot:image>
              <v-img
                gradient="to top right, rgba(19,84,122,.8), rgba(128,208,199,.8)"
              ></v-img>
            </template>

            <template v-slot:prepend>
              <v-app-bar-nav-icon @click="drawer = !drawer"></v-app-bar-nav-icon>
            </template>

            <v-app-bar-title>WEB实验室</v-app-bar-title>

            <v-spacer></v-spacer>

            <v-btn
              :icon="theme === 'light' ? 'mdi-weather-sunny' : 'mdi-weather-night'"
              @click="onToggleTheme"
            ></v-btn>
            <c-translate-menu></c-translate-menu>
          </v-app-bar>

          <v-navigation-drawer v-model="drawer" app temporary>
            <v-list>
              <v-list-item v-for="item of MenuRoutes" :key="item.meta.menu" @click="goto(item)">{{item.meta.menu}}</v-list-item>
            </v-list>
          </v-navigation-drawer>

          <v-main class="position-relative">
            <router-view></router-view>
          </v-main>
        </v-app>
      </v-locale-provider>`,
    setup() {
      const drawer = ref(false);
      const router = VueRouter.useRouter();
      const MenuRoutes = router.options.routes;
      const goto = (route) => {
        router.push({ name: route.name, params: route.params, query: route.query });
      }

      const theme = ref('light');
      function onToggleTheme() {
        theme.value = theme.value === 'light' ? 'dark' : 'light';
      }
      const locale = computed(() => VueI18n.useI18n().locale.value);
      return { drawer, router, goto,MenuRoutes, theme, onToggleTheme, locale };
    }
  };
  const app = createApp(AppConfig);

  const fetchAsyncComponent = async (path) => {
    let res = await import(path);
    return res.default;
  }
  const routes = [
    ...MenuRoutes.map(d => ({ ...d, meta: { menu: d.menu }, component: async () => fetchAsyncComponent(d.fetchPath) }))
  ]
  const router = VueRouter.createRouter({
    history: VueRouter.createWebHashHistory(),
    routes,
  });
  app.use(router);

  const vuetify = createVuetify({
    locale: {
      locale: 'zh_CN',
      fallback: 'en_US',
      messages: i18nMessages,
    },
  });
  app.use(vuetify);

  const i18n = VueI18n.createI18n({
    legacy: false,
    locale: 'zh_CN',
    fallbackLocale: 'en_US',
    messages: {
      en_US: {
        hello: 'hello world'
      },
      zh_CN: {
        hello: '你好世界'
      }
    }
  });
  app.use(i18n);

  const vm = app.mount('#app');
}
const loadMenuRoutes = async () => {
  let res = await axios.get('MenuRoutes.json');
  return res.data;
}
const fetchLocale = async (locale) => await import(`./locale/${locale}.js`);
Promise.all([ loadMenuRoutes(), fetchLocale('zh_CN'), fetchLocale('en_US') ])
  .then(([ MenuRoutes, zhMsg, enMsg ]) => {
    initApp(MenuRoutes, { zh_CN: zhMsg.default, en_US: enMsg.default });
  }).catch(err => {
    console.log('err: ', err);
  });


框架页面

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例</title>
<script src="https://cdn.staticfile.net/vue/3.2.31/vue.global.min.js"></script>
<script src="https://cdn.staticfile.net/vue-router/4.2.4/vue-router.global.min.js"></script>
<script src="https://unpkg.com/vue-i18n@11.0.0/dist/vue-i18n.global.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify@3.8.2/dist/vuetify.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/vuetify@3.8.2/dist/vuetify.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Material+Icons" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet" Cache-Control="max-age=36000" />
</head>
<body>
    <div id="app"></div>
    <script src="App.js"></script>
</body>
</html>

参考Vuetify官方文档通过v-locale-provider包裹根元素并传入locale参数来对所有Vuetify组件支持国际化;但Vuetify默认只支持了en语言,我经过调试取得en的messages,然后适配出简体中文并统一到en_us.js和zh_CN.js文件中(json文件编辑有不少限制,改用js更方便些);抽出Vuetify 组件下拉面板实现的国际化切换组件CTranslateMenu;参考Vuetify官方文档主题切换通过给v-app传入theme参数来实现;因为创建根元素组件需要等路由、国际化配置请求均返回才能创建,故使用Promise.all等依赖都返回后进行创建,因为不能用await所以用then替代。菜单列表采用了导航栏图标点击左出抽屉显示的改进。至此,一个基于Vue3的子页面按需请求加载框架,已基本达到可开发阶段。至于框架页面和子页面混合切换使用中的无障碍联动,通过国际化切换已经可以验证框架页面可以联动子页面了,子页面联动框架页面可考虑在框架页面加面包屑显示子页面菜单,就留给有兴趣的朋友来补充验证了。如果有朋友想参考源代码或服务,下面附上github代码仓和借由它运行的服务(github访问比较慢,可考虑配置host指定可用链路地址,我本机用的是20.200.245.247 github.com 20.205.243.166 github.com仅供参考)
github代码仓:https://github.com/xiangruihua-private/Vue3-Fetch-Async-Page-Example
github服务:https://xiangruihua-private.github.io/Vue3-Fetch-Async-Page-Example/#/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值