用现有bootstrap的模板,改造成nuxt3项目
发布于 8 小时前 作者 shenshuai89 25 次浏览 来自 分享

为了响应快速开发企业网站,并且能够适配移动端,完整的使用tailwind css写一套还挺复杂。

虽然有很多的UI框架,这些框架开发管理系统还可以,有着统一的UI风格,企业网站主要面向C端用户,有着不同设计风格需求,那么之前的bootstrap布局的页面还是很不错的选择。

比如就可以在模板王中下载一套项目代码,通过将内容和文字做一些修改,即可给客户使用。

接下来是改造的过程:

改造最初通过询问AI,给出了2个方案;

  • 第一,使用bootstrap-vue-next,然后配合tailwind css进行改造【使用的此方法,改动量很大,放弃】;
  • 第二,将js和css文件迁移的public目录下,然后在项目中加载,这样只需要将html文件修改为.vue的文件类型,然后修改很少的链接跳转方式即可。

本文中采用第二种方案。

<font style=“background-color:#D9EAFC;”>迁移过程最痛苦的2件事,在vue中js的加载时机 和 迁移静态资源public/assets。</font>

下载代码

本次改造的项目代码,原模板下载https://www.mobanwang.com/mb/demo/22705/

也可以下载其他网络上的优秀的企业站代码。

1213233453.png

├── about.html
├── assets
│   ├── css
│   │   ├── bootstrap.min.css
│   │   ├── em-breadcrumb.css
│   │   ├── plugin_theme_css.css
│   │   └── responsive.css
│   ├── fonts
│   │   ├── Flaticon.woff
│   │   ├── Flaticon.woff2
│   │   ├── Sofia Pro Bold.ttf
│   │   ├── aprova0698.eot
│   │   ├── aprova0698.svg
│   │   ├── aprova0698.ttf
│   │   ├── aprova0698.woff
│   │   ├── fontawesome-webfont3295.ttf
│   │   ├── fontawesome-webfont3295.woff
│   │   ├── fontawesome-webfont3295.woff2
│   │   ├── icofont.eot
│   │   ├── icofont.svg
│   │   ├── icofont.ttf
│   │   ├── icofont.woff
│   │   ├── icofont.woff2
│   │   ├── themify.ttf
│   │   └── themify.woff
│   ├── images
│   │   ├── about-img-1.jpg
│   │   ├── b1.jpg
│   │   ├── b2.jpg
│   │   ├── b3.jpg
│   │   ├── b4.jpg
│   │   ├── b5.jpg
│   │   ├── b6.jpg
│   │   ├── b7.jpg
│   │   ├── b8.jpg
│   │   ├── blog-sidebar1.jpg
│   │   ├── blog-sidebar2.jpg
│   │   ├── blog-sidebar3.jpg
│   │   ├── br1.jpg
│   │   ├── br2.jpg
│   │   ├── br3.jpg
│   │   ├── br4.jpg
│   │   ├── br5.jpg
│   │   ├── contact-bg.jpg
│   │   ├── faq-img.png
│   │   ├── favicon.png
│   │   ├── fottor-bg.jpg
│   │   ├── logo1.png
│   │   ├── logo2.png
│   │   ├── service-bg-img.jpg
│   │   ├── service-img.png
│   │   ├── single-blog.jpg
│   │   ├── single-service.jpg
│   │   ├── skill-img.jpg
│   │   ├── slide-03.jpg
│   │   ├── slider1.jpg
│   │   ├── slider2.jpg
│   │   ├── tab-img.jpg
│   │   ├── tab-img2.jpg
│   │   ├── tab-img3.jpg
│   │   ├── team-bg.jpg
│   │   ├── team1.jpg
│   │   ├── team1.png
│   │   ├── team2.jpg
│   │   ├── team2.png
│   │   ├── team3.jpg
│   │   ├── team3.png
│   │   ├── team4.png
│   │   ├── test1.png
│   │   ├── test2.png
│   │   ├── test3.png
│   │   └── test4.png
│   ├── js
│   │   ├── BeerSlider.js
│   │   ├── ajax-mail.js
│   │   ├── bootstrap.min.js
│   │   ├── bootstrap.min.js.map
│   │   ├── customizer.js
│   │   ├── imagesloaded.pkgd.min.js
│   │   ├── isotope.pkgd.min.js
│   │   ├── jquery.appear.js
│   │   ├── jquery.knob.js
│   │   ├── jquery.meanmenu.js
│   │   ├── jquery.nivo.slider.pack.js
│   │   ├── jquery.waitforimages.js
│   │   ├── map.js
│   │   ├── modernizr.custom.79639.js
│   │   ├── owl.carousel.min.js
│   │   ├── slick.min.js
│   │   ├── swiper-bundle.min.js.map
│   │   ├── theme-pluginjs.js
│   │   ├── theme.js
│   │   └── vendor
│   │       ├── jquery-3.5.1.min.js
│   │       └── modernizr-2.8.3.min.js
│   └── webfonts
│       ├── fa-brands-400.eot
│       ├── fa-brands-400.svg
│       ├── fa-brands-400.ttf
│       ├── fa-brands-400.woff
│       ├── fa-brands-400.woff2
│       ├── fa-regular-400.eot
│       ├── fa-regular-400.svg
│       ├── fa-regular-400.ttf
│       ├── fa-regular-400.woff
│       ├── fa-regular-400.woff2
│       ├── fa-solid-900.eot
│       ├── fa-solid-900.svg
│       ├── fa-solid-900.ttf
│       ├── fa-solid-900.woff
│       └── fa-solid-900.woff2
├── blog-left-sidebar.html
├── blog-right-sidebar.html
├── blog.html
├── contact.html
├── faq.html
├── home-video.html
├── index.html
├── landing-page.html
├── portfolio-3column.html
├── portfolio-4column.html
├── portfolio.html
├── pricing-table.html
├── service.html
├── single-blog.html
├── single-service.html
├── style.css
├── team.html
├── testimonial.html
└── venobox
    ├── close.gif
    ├── next.gif
    ├── preload-circle.png
    ├── preload-dots.png
    ├── preload-ios.png
    ├── preload-quads.png
    ├── preload.png
    ├── prev.gif
    ├── venobox.css
    ├── venobox.js
    └── venobox.min.js

9 directories, 133 files

迁移静态资源

分为3中情况

  • assets下的图片资源,统一存放到public/assets下,后边调整代码来获取该路径的资源
  • 将assets下的js和css和font资源,放到public/assets下的js和css和font

将venobox和style.css文件也迁移到public/assets下,style.css可以放到public/assets/css的目录下,<font style=“color:#DF2A3F;”>注意这里需要将</font><font style="color:#DF2A3F;">style.css</font><font style=“color:#DF2A3F;”>的图片引用,修改为相对引用地址。</font>

  • 将html结尾的文件,复制body部分的代码到vue文件的template中。

关于js脚本加载的问题

在bootstrap中,每个页面为独立html页面,打开都会加载js脚本,并且加载脚本的时间在dom结构渲染完成后进行加载。

那么在改写的vue中,就需要onMounted的生命周期中加载。

在改造过程中尝试了几种方案:

  • 写到plugin中,通过nuxtApp.hook('app:mounted', async () => {})的生命周期时机进行加载,这种方法对index.vue页面生效,但是只加载了一次,对其他页面会失效。
  • 第二种还是想放到插件中,让每个页面的路由之后,加载js
if (process.client) {
  const router = useRouter();

  // 监听路由变化,模拟每个页面的 mounted
  router.afterEach(async (to, from) => {
    console.log('页面 mounted 模拟:', to.path);
    // 在这里执行你的逻辑
    // 加载脚本、埋点、初始化第三方库等
    // const scripts = [
    //   '/js/vendor/modernizr-2.8.3.min.js',
    //   '/js/vendor/jquery-3.5.1.min.js',
    //   '/js/bootstrap.min.js',
    //   '/js/isotope.pkgd.min.js',
    //   '/js/owl.carousel.min.js',
    //   '/js/jquery.nivo.slider.pack.js',
    //   '/js/slick.min.js',
    //   '/venobox/venobox.min.js',
    //   '/js/imagesloaded.pkgd.min.js',
    //   '/js/jquery.appear.js',
    //   '/js/jquery.knob.js',
    //   '/js/BeerSlider.js',
    //   '/js/theme-pluginjs.js',
    //   '/js/jquery.meanmenu.js',
    //   '/js/ajax-mail.js',
    //   '/js/theme.js',
    // ];

    // for (const src of scripts) {
    //   try {
    //     await loadScript(src);
    //     console.log('脚本加载成功:', src);

    //   } catch (err) {
    //     console.error(err);
    //   }
    // }
  });
}

这种方案不能使用<nuxt-link>标签,使用此标签跳转的页面,还是无法正常加载和显示页面。使用<a>时可以生效,但是会刷新页面。

也放弃了这个方案

  • 使用composables下写一个公用的加载js的函数方法,在每个页面的onMounted周期中调用一下,这算是最好的解决办法。
// 缓存已加载的脚本
const loadedScripts = new Set<string>();

const loadScript = (src: string) => {
  return new Promise((resolve, reject) => {
    if (loadedScripts.has(src)) {
      resolve(true);
      return;
    }

    const script = document.createElement('script');
    script.src = src;
    script.defer = true;

    script.onload = () => {
      loadedScripts.add(src);
      resolve(true);
    };

    script.onerror = () => {
      reject(new Error(`Failed to load script: ${src}`));
    };

    document.body.appendChild(script);
  });
};
export const loadScriptClient = async () => {
  const script1 = document.createElement('script');
  script1.src = '/js/vendor/modernizr-2.8.3.min.js';
  document.body.appendChild(script1);

  const script2 = document.createElement('script');
  script2.src = '/js/vendor/jquery-3.5.1.min.js';
  document.body.appendChild(script2);

  const script3 = document.createElement('script');
  script3.src = '/js/bootstrap.min.js';
  document.body.appendChild(script3);

  const script4 = document.createElement('script');
  script4.src = '/js/isotope.pkgd.min.js';
  document.body.appendChild(script4);

  const script5 = document.createElement('script');
  script5.src = '/js/owl.carousel.min.js';
  document.body.appendChild(script5);

  const script6 = document.createElement('script');
  script6.src = '/js/jquery.nivo.slider.pack.js';
  document.body.appendChild(script6);

  const script7 = document.createElement('script');
  script7.src = '/js/slick.min.js';
  document.body.appendChild(script7);

  const script18 = document.createElement('script');
  script18.src = '/venobox/venobox.min.js';
  document.body.appendChild(script18);

  const script8 = document.createElement('script');
  script8.src = '/js/imagesloaded.pkgd.min.js';
  document.body.appendChild(script8);

  const script9 = document.createElement('script');
  script9.src = '/js/jquery.appear.js';
  document.body.appendChild(script9);

  const script10 = document.createElement('script');
  script10.src = '/js/jquery.knob.js';
  document.body.appendChild(script10);

  const script11 = document.createElement('script');
  script11.src = '/js/BeerSlider.js';
  document.body.appendChild(script11);

  const script12 = document.createElement('script');
  script12.src = '/js/theme-pluginjs.js';
  document.body.appendChild(script12);

  const script13 = document.createElement('script');
  script13.src = '/js/jquery.meanmenu.js';
  document.body.appendChild(script13);

  const script14 = document.createElement('script');
  script14.src = '/js/ajax-mail.js';
  document.body.appendChild(script14);

  const script15 = document.createElement('script');
  script15.src = '/js/theme.js';
  document.body.appendChild(script15);
  
};

在vue的页面中进行调用

// 初始化脚本
onMounted(() => {
  loadScriptClient();
})

关于图片资源引用的问题

public/css内部应用的图片路径地址

public/assets/css内部应用的图片路径地址,是public/assets/images中的资源;

.consit_service_area2 {
    background-image: url("../images/port-bg-img.jpg");
    background-repeat: no-repeat;
    background-size: cover;
    padding: 120px 0px 110px;
}

vue文件的template的图片

template模版内的图片引入:

  • 使用"/assets/images/logo1.png"的也是public/assets/images目录的资源。
  • 如果代码"assets/images/logo1.png"则是使用了assets/images目录资源
<template>
  <div class="mobile_menu_logo text-center">
    <a href="index.html" title="consit">
      <img src="/assets/images/logo1.png" alt="consit" />
    </a>
  </div>
</template>

vue文件的template的style中使用图片

在template中的style添加背景图片,这里如果正常使用/assets/images/logo1.png,就会引用到/assets/images的资源,而不是public/assets/image的资源。

下面代码引用了 assets/image的资源

这里不管有没有 / 路径,都是使用的 assets/image

<template>
  <div
      class="swiper-slide d1 t1 m1 witr_swiper_height"
      style="background-image:	url(/assets/images/slider1.jpg)"
    >
    1111
  </div>
</template>

可以结合script,使用public的资源;

主要思路是通过动态导入图片资源,然后在绑定到style中

<template>
  <div
      class="swiper-slide d1 t1 m1 witr_swiper_height"
      :style="{ backgroundImage: `url(${slider1})` }"
    >
    1111
  </div>
</template>
<script setup>
  // vue script内引入assets图片的方法
  import slider1 from '/assets/images/slider1.jpg';
</script>

迁移完成后代码结构

./
├── README.md
├── app.vue
├── components
│   ├── navFooter.vue
│   └── navHeader.vue
├── composables
│   └── index.ts
│── nuxt.config.ts
├── package.json
├── pages
│   ├── about.vue
│   ├── blog.vue
│   ├── blogLeft.vue
│   ├── blogRight.vue
│   ├── contact.vue
│   ├── faq.vue
│   ├── homeVideo.vue
│   ├── index.vue
│   ├── landingPage.vue
│   ├── portfolio.vue
│   ├── portfolio3column.vue
│   ├── portfolio4column.vue
│   ├── pricingTable.vue
│   ├── service.vue
│   ├── serviceSingle.vue
│   ├── singleBlog.vue
│   ├── team.vue
│   └── testimonial.vue
├── plugins
│   └── load-script.client.ts
├── pnpm-lock.yaml
├── public
│   ├── assets
│   │   └── images
│   │       ├── about-img-1.jpg
│   │       ├── b1.jpg
│   │       ├── b2.jpg
│   │       ├── b3.jpg
│   │       ├── b4.jpg
│   │       ├── ...
│   │       ├── css
│   │       │   ├── bootstrap.min.css
│   │       │   ├── em-breadcrumb.css
│   │       │   ├── plugin_theme_css.css
│   │       │   ├── responsive.css
│   │       │   └── style.css
│   │       ├── fonts
│   │       │   ├── Flaticon.woff
│   │       │   ├── ...
│   │       ├── js
│   │       │   ├── BeerSlider.js
│   │       │   ├── ajax-mail.js
│   │       │   ├── ...
│   │       ├── venobox
│   │       │   ├── close.gif
│   │       │   ├── next.gif
│   │       │   ├── preload-circle.png
│   │       │   ├── preload-dots.png
│   │       │   ├── preload-ios.png
│   │       │   ├── preload-quads.png
│   │       │   ├── preload.png
│   │       │   ├── prev.gif
│   │       │   ├── venobox.css
│   │       │   ├── venobox.js
│   │       │   └── venobox.min.js
│   │       └── webfonts
│   │           ├── fa-brands-400.eot
│   │           ├── fa-brands-400.svg
│   │           ├── ... 
│   ├── favicon.ico
│   ├── favicon.png
│   ├── robots.txt
└── tsconfig.json

29 directories, 146 files

以上是将bootstrap项目转为nuxt项目的代码结构。

关于接口调用和打包发布上线的操作

详细的操作和项目代码仓库,放置到个人网站中,如有需要请查看;

回到顶部