16

Nuxtjs3 重写 blog 过程备忘

 2 years ago
source link: https://blog.kelu.org/tech/2022/05/10/nuxtjs.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Nuxtjs3 重写 blog 过程备忘

2022-05-10     tech vue fron-tend nuxtjs
vue.jpg

作为一个本职是基础设施开发的后端人员,我将js比作linux系统,将vue比作docker/containerd,而nuxt就相当于kubernetes了。当然这个类比并不是非常准确。前端比容器大战要柔和的多,感觉大家都在底层上重复建设。对比容器技术,swarm/k8s/mesos也就5年左右就分出了胜负。前端目前主流还是react/vue/Angular这三家,基于这三家之上又有各自的服务端框架(更底层typescript倒是已经统一了),导致前端学习门槛较高,框架概念、设计模式其实也走了一遍后端框架的老路,很多概念后端开发可以平移过来。我比较相信权威,相信Taylor Otwell(Laravel的创造者),他说vue好,所以我就跟着他一起用了。

前两篇记录了点vue2vue3的笔记,这一篇记录下nuxtjs3,这是官网,2022 年 4 月 20 日正式发布 nuxt3,代号为“Mount Hope”

Nuxt3是基于 Vue3 发布的 SSR 框架:

  • SPA应用:也就是单页应用,这些多是在客户端的应用,不能进行SEO优化(搜索引擎优化)。
  • SSR应用:在服务端进行渲染,渲染完成后返回给客户端,每个页面有独立的URL,对SEO友好,对应企业网站、商品展示 、博客这类型的展示型网站。

这是官网显示的一些优势说明:

  • 更轻量:以现代浏览器为基础的情况下,服务器部署和客户端产物最多减小75倍。
  • 更快:用动态服务端代码来优化冷启动。
  • Hybird:增量动态生成和其他高级模式现在都成为可能。
  • Suspense: 导航前后可在任何组件中获取数据。
  • Composition API : 使用Composition API 和 Nuxt3的composables 实现真正的可复用性。
  • Nuxt CLI : 权限的零依赖体验,助你轻松搭建项目和集成模块。
  • Nuxt Devtools :专属调试工具,更多的信息和快速修复,在浏览器中高效工作。
  • Nuxt Kit :全新的基于 TypeScript 和跨版本兼容的模块开发。
  • Webpack5 : 更快的构建速度和更小的构建包,并且零配置。
  • Vite:用Vite作为你的打包器,体验轻量级的快速HMR。
  • Vue3 : 完全支持Vue3语法,这一点特别关键。
  • TypeScript:由原生TypeScript和ESM构成,没有额外配置步骤。

npx是npm从5.2版开始增加的命令

官方链接

npx nuxi init nuxt3-test  # 快速创建
image-20220517113727498
npm install
npm run dev
image-20220517113912229

生成文件如下:

image-20220517114211393

这里要重点提的是 nuxt.config.js 文件,所有配置都在里面。

一些常用的目录如下:

- pages               // 页面目录,Nuxt.js 框架读取该目录下所有的 .vue 文件并自动生成对应的路由配置。
- components          // 组件目录
- assets              // 静态资源目录
- public              // 不需要 vue 处理的静态资源,此类文件不会被 Nuxt.js 调用 Webpack 进行构建编译处理。 服务器启动的时候,该目录下的文件会映射至应用的根路径 / 下。
- layouts             // 项目布局目录
- common              // 存储js文件
- plugins             // nuxt扩展的js文件
- store               // vuex的配置文件,Nuxt.js 框架集成了 Vuex 状态树 的相关功能配置,在 store 目录下创建一个 index.js 文件可激活这些配置。

配置参考链接:

删除根目录下的app.vue,然后pages目录下新建index.vue文件,就会自动关联vue-router,支持动态路由。

因为我是容器化环境开发的,所以改了一下运行端口到80端口,访问方便一些。如果发现配置不生效,可以尝试重启一卡容器(多个容器在同一个目录下启动npm run时感觉会冲突,就遇到有些配置不生效了)。

image-20220519113053640

一些官网备忘

head

setup函数中,可以useHead使用与元标记对应的键的元属性对象进行调用:title, titleTemplate, base, script, style, metaand link, 以及htmlAttrsand bodyAttrs。还有两个速记属性charsetviewport

data-fetching

Nuxt 提供useFetchuseLazyFetchuseAsyncDatauseLazyAsyncData处理应用程序中的数据获取。

state-management

useState是一个对 SSR 友好的ref 替代品。它的值将在服务器端渲染后(在客户端水化期间)被保留,并使用唯一键在所有组件之间共享。

server-routes

Nuxt 会自动扫描 、 和 目录中的文件~/server/api~/server/routes~/server/middleware注册具有 HMR 支持的 API 和服务器处理程序。

每个文件都应该导出一个用defineEventHandler().

Nuxt使用了自动加载路由的方案,他会根据你项目的目录来自动创建router对象;

一个简单的示例,pages下的目录示例及其对应的路由如下所示:

test:
  a.vue     -- 对应路径:/test/a
  index.vue -- 对应路由:/test
index:
  _id:
    a.vue      -- 对应路由:/{id}/a
    index.vue  -- 对应路由:/{id}
  index.vue    -- 对应路由:/,会被包含到上一级的index.vue的<nuxt-child>中
index.vue      -- 对应路由:/,其中如果包含有nuxt-child元素,那么会将index目录下的index.vue加载到nuxt-child元素位置  
  • 在 Nuxt.js 里面定义带参数的动态路由,需要创建对应的以下划线作为前缀的 Vue 文件 或 目录。
  • 如果你想将_id设置为必选的路由,需要在 users/_id 目录内创建一个 index.vue 文件。

路由参数校验

Nuxt.js 可以让你在动态路由组件中定义参数校验方法。

举个例子: pages/users/_id.vue

export default {
  validate ({ params }) {
    // 必须是number类型
    return /^\d+$/.test(params.id)
  }
}

如果校验方法返回的值不为 true或Promise中resolve 解析为false或抛出Error , Nuxt.js 将自动加载显示 404 错误页面或 500 错误页面。

未知嵌套深度的动态嵌套路由

如果您不知道URL结构的深度,您可以使用_.vue动态匹配嵌套路径。这将处理与更具体请求不匹配的情况。

文件结构:

pages/
--| people/
-----| _id.vue
-----| index.vue
--| _.vue
--| index.vue

一些代码只是为了做代码的结构备忘,不代表逻辑正确。

前端的语法糖太多了,我觉得这些语法糖是前端晦涩的一大原因。

不过,“无他,但手熟尔”,在前端也是一样。

  1. 命令行nuxi

    npx nuxi init nuxt3-test  # 快速创建
    
  2. layout 布局 html(静态)

    layout文件夹
    page文件夹
    
  3. 引入 css

    <style scope>
    .abc {}
    </style>
    
  4. 应用状态state

    <script setup>
      const options = reactive({
          xxx: "xxx"
      });
    </script>
       
    <template>
      <button class="abc" :class="options.xxx === 'xxx' && 'option-active'">abc</button>
    </template>
    
  5. a标签替换为 NuxtLink 标签

    <NuxtLink to="/">Home</NuxtLink>
    
  6. 简单的typescript

    我觉得typescript的语法有点像golang。 看着也挺舒服的~

    先定义个interface类型,一些选项/常量用enum定义,再const一个实例。

    <script setup lang="ts">
      interface OptionState {
        xxx: string,
        yyy: string
      }
         
      enum Xxx {
        AAA = "aaa",
        BBB = "bbb"
      }
       
      const obj := OptionState{
          xxx: Xxx.AAA,
          yyy: Xxx.BBB
      }
         
      const options = reactive<OptionState>({
          xxx: Xxx.AAA,
          yyy: Xxx.BBB
      });
    </script>
    
  7. import 其他文件里定义的内容

    import {OptionState} from "@/data"  // 其他文件要 export特定的定义
    

    教程有的为了简化内容,直接准备一个json文件,然后import数据进来:

    <script setup lang="ts">
    import {xxx} from "@/data.json"
         
    const rest = {
      first: [...xxx].splice(0,25),
      second: [...xxx].spice(25,25),
    };
    </script>
         
    <template>
      <Row v-for="r in rest.first" :key="r.id" />
      <Row v-for="r in rest.second" :key="r.id" />
    </template>
         
    
  8. 更新state

    <script setup lang="ts">
      const computeSelectedNames = () => {
        const filterNames = names
        	.filter((name) => name.xxx === options.xxx )
        	.filter((name) => name.yyy === options.yyy )
        	.filter((name) => {
            return ...
          })
             
          selectdNames.value = filterdNames.map((name) => name.xxx )
           
      };
      const selectdNames = ref<string[]>{[]}
    </script>
       
    <template>
      <button class="abc" @click="computeSelectName">Find</button>
      {{ selectedNames }}
    </template>
    
  9. 注意拆分component,以及通过props在父子组件中传值

    <script setup lang="ts">
      interface OptionProps{
        option: {
            id: string,
          	title: string,
            catagory: string,
            index: number
        };
      }
         
      const props = defindProps<OptionProps>()
    </script>
    

    在父组件中传入:

    ```vue

大写开头的标签就是component,有一些是 nuxt 保留的,用来作为头部header内容的,类似于 metadata

  1. 通过事件触发父子组件传值。

    // 父:
    <script setup lang="ts">
      const removeName = (index: number) => {
        const filteredNames = [...selectdNames.value]
        filterNames.splice(index,1)
        selectedName.value = filterNames 
      }
    </script>
        
    <template>
      <CardName v-for="(name, index) in selectedNames" :key="name" :name="name" @remove="()=> removeName(index)" :index="index" />
    </template>
        
    // 子CardName:
    <script setup lang="ts">
      interface NameProps{
        name: string;
        index: number
      }
          
      const props = defindProps<NameProps>();
      const emit = defindEmits(["remove"])
          
      const removeName = () => {
        emit("remove", props.index)
      }
    </script>
        
    <template>
      <p @click="removeName">xxx </p>
    </template>
    
  2. 常用的循环语句

    v-for
    
  3. Pages, 基于文件的路由route

    <script setup lang="ts">
    // import {}  from "#app"; // 用来让编辑器不报错的,其实没什么用。
    import xxx from "@/data.json"
          
    const route = useRoute();
    const name = route.params.name;
          
    const x = xxx.find(r => r.name === name)
    </script>
        
    <template>
    {{ name }}
    </template>
    
  4. 404页面

  5. Layouts

    创建 layouts 文件夹,并创建default.vue文件。

    在page中声明使用特定的layout

    <template>
      <div>
        <NuxtLayout name="custom">
          <Xxx />
        </NuxtLayout>
      </div>
    </template>
    

    在layout中插入slot(假设layout名error)

    <template>
      <div>
        <slot name="xxx" />
        <div>...</div>
      </div>
    </template>
    

    在page中:

    <template>
      <div>
        <NuxtLayout name="error">
          <template #xxx>
            <h1></h1>
          </template>
        </NuxtLayout>
        <div>...</div>
      </div>
    </template>
    
  6. metadata

    image-20220523155833406

    为了方便控制,也可以写在script里,

    image-20220523160139589

    也可以使用 outside stylesheets 方式:

    在nuxt.config.js里配置。

    export default defineNuxtConfig({
        charset: "utf-8",
        viewport: "width=device-width, initial-scale=1, maximum-scale=1",
        meta: {
            link: [
                { rel: "stylesheet", href: "/assets/css/style.css" },
                { rel: "stylesheet", href: "/assets/css/font-awesome.min.css" },
            ],
            script: [
                { src: '/assets/js/jquery.min.js' },
              ],
        }
    })
    
  7. 网页数据状态设计

  8. composable,useState

    用来保存状态。不能用 ref 做跟踪,因为这里没有render,会造成内存泄漏。

    image-20220523162132417
    image-20220523162252693
  9. Global State

    image-20220523162655826
    image-20220523162728965
  10. 生成静态网站


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK